初始化

This commit is contained in:
2026-03-02 13:44:38 +08:00
commit 05b785083c
677 changed files with 58662 additions and 0 deletions

View File

@@ -0,0 +1,240 @@
<?php
namespace app\api\controller\v1;
use addons\webman\model\DrawRecord;
use addons\webman\model\Game;
use addons\webman\model\GamePlatform;
use addons\webman\model\Prize;
use app\exception\GameException;
use app\exception\PlayerCheckException;
use app\service\DrawService;
use app\service\game\GameServiceFactory;
use Illuminate\Support\Str;
use Respect\Validation\Exceptions\AllOfException;
use Respect\Validation\Validator as v;
use support\Request;
use support\Response;
class GameController
{
/** 排除验签 */
protected $noNeedSign = ['gametest'];
/**
* 登录游戏返回游戏地址
* @param Request $request
* @return Response
* @throws PlayerCheckException|GameException
*/
public function loginGame(Request $request): Response
{
$player = checkPlayer();
$validator = v::key('platform_id', v::notEmpty()->intVal()->setName(trans('platform_id', [], 'message')))
->key('game_code', v::stringVal()->setName(trans('game_code', [], 'message')))
->key('app_type', v::notEmpty()->intVal()->in([
GameServiceFactory::DEVICE_TYPE_WEB,
GameServiceFactory::DEVICE_TYPE_IOS,
GameServiceFactory::DEVICE_TYPE_ANDROID,
])->setName(trans('app_type', [], 'message')));
$data = $request->all();
/** @var GamePlatform $gamePlatform */
$gamePlatform = GamePlatform::query()->find($data['platform_id']);
if (empty($gamePlatform)) {
return jsonFailResponse(trans('game_platform_not_found', [], 'message'));
}
if ($gamePlatform->status == 0) {
return jsonFailResponse(trans('game_platform_disabled', [], 'message'));
}
/** @var Game $game */
$game = Game::query()
->whereHas('gamePlatform', function ($query) {
$query->where('status', 1)->whereNull('deleted_at');
})
->where('platform_id', $data['platform_id'])
->where('game_code', $data['game_code'])
->first();
if (empty($game)) {
return jsonFailResponse(trans('game_not_found', [], 'message'));
}
if ($game->status == 0) {
return jsonFailResponse(trans('game_disabled', [], 'message'));
}
$lang = locale();
$lang = Str::replace('_', '-', $lang);
try {
$validator->assert($data);
$res = GameServiceFactory::createService(strtoupper($gamePlatform->name), $player)->login(
[
'CallBackUrl' => '',
'lang' => $lang,
'gameCode' => $game->game_code,
'appType' => $data['app_type'],
'platformGameType' => $game->platform_game_type
]
);
if ($player->wallet->money > 0) {
GameServiceFactory::createService(strtoupper($gamePlatform->name), $player)->balanceTransferOut();
}
} catch (AllOfException $e) {
return jsonFailResponse(getValidationMessages($e));
} catch (\Exception $e) {
return jsonFailResponse($e->getMessage());
}
if ($gamePlatform->name == 'MEGA888') {
$response = [
'link_game' => $res['url'],
'jump_url' => 'lobbymegarelease://',
'jump_url_android' => 'lobbymegarelease://?account='.$res['account'].'&password='.$res['password'],
'jump_url_ios' => 'lobbymegarelease://account='.$res['account'].'&password='.$res['password'],
'account' => $res['account'],
'password' => $res['password'],
];
} elseif ($gamePlatform->name == 'KISS918') {
$response = [
'link_game' => $res['url'],
'jump_url_android' => 'lobbykiss://lobbykiss?account='.$res['account'].'&password='.$res['password'],
'jump_url_ios' => 'lobbykissgame://account='.$res['account'].'&password='.$res['password'],
'account' => $res['account'],
'password' => $res['password'],
];
} else {
$response = ['link_game' => $res];
}
return jsonSuccessResponse('success',$response);
}
/**
* 游戏列表
* @param Request $request
* @return Response
* @throws PlayerCheckException|GameException
*/
public function gameList(Request $request): Response
{
checkPlayer();
$validator = v::key('game_type', v::notEmpty()->intVal()->setName(trans('game_type', [], 'message')), false)
->key('is_hot', v::optional(v::in([0, 1]))->setName(trans('is_hot', [], 'message')), false);
$data = $request->all();
try {
$validator->assert($data);
} catch (AllOfException $e) {
return jsonFailResponse(getValidationMessages($e));
}
$size = $data['size'] ?? 20;
$query = Game::query()
->with(['gamePlatform' => function ($query) {
$query->select(['id', 'name', 'status']);
}])
->whereHas('gamePlatform', function ($query) {
$query->where('status', 1)->whereNull('deleted_at');
})
->where('status', 1)
->where('is_online', 1)
->when(!empty($data['game_type']), function ($query) use ($data) {
$query->where('game_type', $data['game_type']);
})
->when(isset($data['is_hot']) && is_numeric($data['is_hot']), function ($query) use ($data) {
$query->where('is_hot', $data['is_hot']);
})
->when(isset($data['platform_id']) && is_numeric($data['platform_id']), function ($query) use ($data) {
$query->where('platform_id', $data['platform_id']);
});
$list = $query->select(['id', 'platform_id', 'game_code', 'game_type', 'game_image', 'name', 'status', 'player_num_range', 'is_hot', 'is_new', 'sort'])
->forPage($data['page'] ?? 1, $size)
->orderBy('sort', 'desc')
->orderBy('id', 'desc')
->get();
$totalGames = $query->count();
/** @var Game $game */
foreach ($list as $game) {
$game->player_num_range = empty($game->player_num_range) ? 0 : getGamePlayerNum($game->id, $game->player_num_range);
}
return jsonSuccessResponse('success', [
'list' => $list,
'game_platform' => GamePlatform::query()->where('status', 1)->select(['id', 'title'])->get(),
'current_page' => $data['page'] ?? 1,
'total_page' => ceil($totalGames / $size)
]);
}
/**
* 奖品列表
*/
public function getPrizeList(Request $request)
{
$player = checkPlayer();
$prizes = Prize::query()
->select('pic', 'name', 'id', 'type')
->where('department_id', $player->department_id)
->where('status', 1)
->orderBy('probability')
->get()
->toArray();
$data = [
'point' => $player->wallet->money,
'description' => Game::query()->where('id', $player->channel->game_id)->value('description'),
'prize_list' => $prizes,
];
return jsonSuccessResponse('success',$data);
}
/**
* 抽奖
*/
public function lottery(Request $request): Response
{
$player = checkPlayer();
if ($player->wallet->money <= 0) {
return jsonFailResponse('玩家暂无抽奖机会', [], 1000);
}
$drawService = new DrawService();
$result = $drawService->execute($player, $player->channel->game_id, $player->department_id, $request->getRealIp());
return jsonSuccessResponse('success',$result);
}
/**
* 抽奖记录
*/
public function getDrawRecords(Request $request): Response
{
$player = checkPlayer();
$validator = v::key('page', v::intVal()->setName(trans('page', [], 'message')))
->key('size', v::intVal()->setName(trans('size', [], 'message')))
->key('start_date', v::stringVal()->setName(trans('start_date', [], 'message')))
->key('end_date', v::stringVal()->setName(trans('end_date', [], 'message')));
$data = $request->all();
try {
$validator->assert($data);
} catch (AllOfException $e) {
return jsonFailResponse(getValidationMessages($e));
}
$size = $data['size'] ?? 20;
$query = DrawRecord::query()
->where('department_id', $player->department_id)
->where('uid', $player->id)
->where('game_id', $player->channel->game_id)
->whereBetween('created_at', [$data['start_date'],date('Y-m-d', strtotime("{$data['end_date']} +1 days"))]);
$record = clone $query;
$list = $query
->select('consume', 'prize_pic', 'prize_name', 'prize_type', 'draw_time', 'ip')
->forPage($data['page'] ?? 1, $data['size'] ?? 10)
->orderBy('id', 'desc')
->get()
->toArray();
$totalRecords = $record->count();
return jsonSuccessResponse('success', [
'list' => $list,
'total_page' => ceil($totalRecords / $size)
]);
}
public function gametest(Request $request): Response
{
return jsonSuccessResponse('', []);
}
}

View File

@@ -0,0 +1,751 @@
<?php
namespace app\api\controller\v1;
use addons\webman\model\Activity;
use addons\webman\model\ActivityContent;
use addons\webman\model\BankList;
use addons\webman\model\Broadcast;
use addons\webman\model\Channel;
use addons\webman\model\ChannelRechargeSetting;
use addons\webman\model\Currency;
use addons\webman\model\Notice;
use addons\webman\model\PlayerBank;
use addons\webman\model\PlayerDeliveryRecord;
use addons\webman\model\PlayerRechargeRecord;
use addons\webman\model\PlayerWalletTransfer;
use addons\webman\model\PlayerWithdrawRecord;
use app\exception\GameException;
use app\exception\PlayerCheckException;
use Exception;
use Illuminate\Support\Carbon;
use Illuminate\Support\Str;
use Respect\Validation\Exceptions\AllOfException;
use Respect\Validation\Validator as v;
use support\Db;
use support\Request;
use support\Response;
use Tinywan\Jwt\JwtToken;
class IndexController
{
/** 排除验签 */
protected $noNeedSign = [];
/**
* 登出接口
* @return Response
* @throws PlayerCheckException|GameException
*/
public function logout(): Response
{
checkPlayer();
if (JwtToken::clear()) {
return jsonSuccessResponse('success');
}
return jsonFailResponse(trans('logout_failed', [], 'message'));
}
/**
* 添加银行卡
* @param Request $request
* @return Response
* @throws GameException
* @throws PlayerCheckException
*/
public function addBankCard(Request $request): Response
{
$player = checkPlayer();
$data = $request->all();
$validator = v::key('bank_name', v::optional(v::stringType()->length(1, 100)->setName(trans('bank_name', [], 'message'))))
->key('account', v::optional(v::stringType()->length(1, 255)->setName(trans('bank_account', [], 'message'))))
->key('account_name', v::optional(v::stringType()->length(1, 100)->setName(trans('bank_account_name', [], 'message'))))
->key('bank_code', v::optional(v::stringType()->length(1, 100)->setName(trans('bank_code', [], 'message'))))
->key('wallet_address', v::optional(v::stringType()->length(1, 255)->setName(trans('wallet_address', [], 'message'))))
->key('qr_code', v::optional(v::stringType()->setName(trans('qr_code', [], 'message'))));
try {
$validator->assert($data);
} catch (AllOfException $e) {
return jsonFailResponse(getValidationMessages($e));
}
if (!empty($data['account']) && PlayerBank::query()->where('account', $data['account'])
->where('pay_type', $player->channel->pay_type)->exists()) {
return jsonFailResponse(trans('bank_card_has_bind', [], 'message'));
}
$payType = $player->channel->pay_type;
//USDT判断
if (!empty($data['wallet_address'])) {
$payType = 4;
if (PlayerBank::query()->where('wallet_address', $data['wallet_address'])->where('pay_type', 4)->exists()) {
return jsonFailResponse(trans('bank_card_has_bind', [], 'message'));
}
}
$bankNum = PlayerBank::query()
->where('player_id', $player->id)
->where('status', 1)
->where('pay_type', $player->channel->pay_type)
->count();
if ($bankNum > 2) {
return jsonFailResponse(trans('bank_card_max_three', [], 'message'));
}
$playerBank = new PlayerBank();
$playerBank->player_id = $player->id;
$playerBank->bank_name = $data['bank_name'];
$playerBank->account = $data['account'];
$playerBank->account_name = $data['account_name'];
$playerBank->wallet_address = $data['wallet_address'] ?? '';
$playerBank->qr_code = $data['qr_code'] ?? '';
$playerBank->bank_code = $data['bank_code'];
$playerBank->pay_type = $payType;
if (!$playerBank->save()) {
return jsonFailResponse(trans('add_bank_card_fail', [], 'message'));
}
return jsonSuccessResponse(trans('add_bank_card_success', [], 'message'));
}
/**
* 修改银行卡
* @param Request $request
* @return Response
* @throws GameException
* @throws PlayerCheckException
*/
public function editBankCard(Request $request): Response
{
$player = checkPlayer();
$data = $request->all();
$validator = v::key('bank_name', v::notEmpty()->stringType()->length(1, 100)->setName(trans('bank_name', [], 'message')))
->key('account_name', v::notEmpty()->stringType()->length(1, 100)->setName(trans('bank_account_name', [], 'message')))
->key('bank_code', v::notEmpty()->stringType()->length(1, 100)->setName(trans('bank_code', [], 'message')))
->key('id', v::notEmpty()->stringType()->length(1, 100)->setName(trans('id', [], 'message')));
try {
$validator->assert($data);
} catch (AllOfException $e) {
return jsonFailResponse(getValidationMessages($e));
}
if (PlayerBank::query()->where('player_id', $player->id)->where('status', 1)->count() > 2) {
return jsonFailResponse(trans('bank_card_max_three', [], 'message'));
}
$playerBank = PlayerBank::query()->find($data['id']);
if ($playerBank->player_id != $player->id) {
return jsonFailResponse(trans('edit_bank_card_fail', [], 'message'));
}
$playerBank->bank_name = $data['bank_name'];
$playerBank->account = $data['account'];
$playerBank->account_name = $data['account_name'];
$playerBank->bank_code = $data['bank_code'];
if (!$playerBank->save()) {
return jsonFailResponse(trans('edit_bank_card_fail', [], 'message'));
}
return jsonSuccessResponse(trans('edit_bank_card_success', [], 'message'));
}
/**
* 银行卡列表
* @param Request $request
* @return Response
* @throws PlayerCheckException|GameException
*/
public function bankCardList(Request $request): Response
{
$player = checkPlayer();
$data = $request->all();
$validator = v::key('page', v::intVal()->setName(trans('page', [], 'message')))
->key('size', v::intVal()->setName(trans('size', [], 'message')));
try {
$validator->assert($data);
} catch (AllOfException $e) {
return jsonFailResponse(getValidationMessages($e));
}
return jsonSuccessResponse('success', [
'bank_list' => PlayerBank::query()
->where('player_id', $player->id)
->where('status', 1)
->where('pay_type', $player->channel->pay_type)
->whereNull('deleted_at')
->select(['id', 'bank_name', 'account', 'account_name'])
->forPage($data['page'] ?? 1, $data['size'] ?? 10)
->orderBy('created_at', 'desc')
->get()
->toArray(),
]);
}
/**
* 银行卡列表
* @return Response
* @throws PlayerCheckException|GameException|\think\Exception
*/
public function bankList(): Response
{
$player = checkPlayer(false);
return jsonSuccessResponse('success', [
'bank_list' => BankList::query()
->select(['bank_name', 'bank_code'])
->where('pay_type', $player->channel->pay_type)
->where('type', '!=', 1)
->whereNull('deleted_at')
->get()
->toArray(),
]);
}
/**
* 删除银行卡
* @param Request $request
* @return Response
* @throws PlayerCheckException|GameException
*/
public function deleteBankCard(Request $request): Response
{
$player = checkPlayer();
$data = $request->all();
$validator = v::key('id', v::intVal()->notEmpty()->setName(trans('bank_card_id', [], 'message')));
try {
$validator->assert($data);
} catch (AllOfException $e) {
return jsonFailResponse(getValidationMessages($e));
}
$bankCard = PlayerBank::withTrashed()->where('player_id', $player->id)->where('id', $data['id'])->first();
if (!$bankCard) {
return jsonFailResponse(trans('bank_card_not_found', [], 'message'));
}
if ($bankCard->status == 0) {
return jsonFailResponse(trans('bank_card_disabled', [], 'message'));
}
if (!empty($bankCard->deleted_at)) {
return jsonFailResponse(trans('bank_card_deleted', [], 'message'));
}
$bankCard->delete();
return jsonSuccessResponse(trans('success', [], 'message'));
}
/**
* 上传支付凭证
* @param Request $request
* @return Response
* @throws GameException
* @throws PlayerCheckException
*/
public function uploadCertificate(Request $request): Response
{
checkPlayer();
$file = $request->file('certificate');
$filePath = '';
if ($file && $file->isValid()) {
if ($file->getSize() > 3 * 1024 * 1024) {
return jsonFailResponse(trans('image_upload_size_fail', ['{size}' => '3M'], 'message'));
}
$allowedExtensions = ['png', 'jpg', 'jpeg'];
$extension = $file->getUploadExtension();
if (!in_array($extension, $allowedExtensions)) {
return jsonFailResponse(trans('image_upload_fail', [], 'message'));
}
$savePath = '/storage/certificate/' . date("Ymd", time()) . "/";
$newPath = public_path() . $savePath;
if (!file_exists($newPath)) {
//检查是否有该文件夹,如果没有就创建,并给予最高权限
mkdir($newPath, 0755, true);
}
$extension = $file->getUploadExtension();
$filename = time() . '_' . uniqid() . ".{$extension}"; //文件名
$newPath = $newPath . $filename;
$file->move($newPath);
$filePath = env('APP_URL', 'http://127.0.0.1:8787') . $savePath . $filename;
}
if (!$filePath) {
return jsonFailResponse(trans('failed_to_upload_recharge_voucher', [], 'message'));
}
return jsonSuccessResponse('success', ['file_path' => $filePath]);
}
/**
* 玩家充值
* @param Request $request
* @return Response
* @throws PlayerCheckException
* @throws Exception
*/
public function playerRecharge(Request $request): Response
{
$player = checkPlayer();
$data = $request->all();
$validator = v::key('id', v::notEmpty()->intVal()->setName(trans('recharge_setting_id', [], 'message')))
->key('certificate', v::notEmpty()->url()->notEmpty()->setName(trans('certificate', [], 'message')));
try {
$validator->assert($data);
} catch (AllOfException $e) {
return jsonFailResponse(getValidationMessages($e));
}
$rechargeRecord = PlayerRechargeRecord::where('player_id', $player->id)
->where('status', PlayerRechargeRecord::STATUS_WAIT)
->where('type', PlayerRechargeRecord::TYPE_REGULAR)
->first();
if (!empty($rechargeRecord)) {
return jsonFailResponse(trans('has_not_unfinished_recharge', [], 'message'));
}
/** @var Channel $channel */
$channel = Channel::where('department_id', $player->department_id)->first();
if (empty($channel)) {
return jsonFailResponse(trans('channel_not_found', [], 'message'));
}
if ($channel->recharge_status == 0) {
return jsonFailResponse(trans('recharge_closed', [], 'message'));
}
$lang = locale();
$lang = Str::replace('_', '-', $lang);
/** @var ChannelRechargeSetting $channelRechargeSetting */
$channelRechargeSetting = ChannelRechargeSetting::with(['channel_recharge_method' => function ($query) use ($lang) {
$query->select(['id', 'account', 'currency'])->with(['methodLang' => function ($query) use ($lang) {
$query->select(['id', 'bank_name', 'sub_bank', 'owner', 'name', 'method_id', 'lang'])->where('lang', $lang);
}]);
}])->whereHas('channel_recharge_method', function ($query) {
$query->whereNull('deleted_at')->where('status', 1);
})
->where('department_id', $player->department_id)
->where('status', 1)
->where('id', $data['id'])
->whereNull('deleted_at')
->first();
if (empty($channelRechargeSetting)) {
return jsonFailResponse(trans('channel_recharge_setting_not_found', [], 'message'));
}
try {
$allCoins = bcadd($channelRechargeSetting->coins_num, $channelRechargeSetting->gift_coins, 2);
// 生成充值订单
$playerRechargeRecord = new PlayerRechargeRecord();
$playerRechargeRecord->player_id = $player->id;
$playerRechargeRecord->department_id = $player->department_id;
$playerRechargeRecord->tradeno = createOrderNo();
$playerRechargeRecord->player_name = $player->name ?? '';
$playerRechargeRecord->money = $channelRechargeSetting->money;
$playerRechargeRecord->inmoney = $channelRechargeSetting->money;
$playerRechargeRecord->setting_id = $channelRechargeSetting->id;
$playerRechargeRecord->coins = $channelRechargeSetting->coins_num;
$playerRechargeRecord->gift_coins = $channelRechargeSetting->gift_coins;
$playerRechargeRecord->currency = $channelRechargeSetting->channel_recharge_method->currency;
$playerRechargeRecord->type = $channelRechargeSetting->type;
$playerRechargeRecord->status = PlayerRechargeRecord::STATUS_RECHARGING;
$playerRechargeRecord->certificate = $data['certificate'] ?? '';
$playerRechargeRecord->bank_name = $channelRechargeSetting->channel_recharge_method->methodLang[0]['bank_name'] ?? '';
$playerRechargeRecord->sub_bank = $channelRechargeSetting->channel_recharge_method->methodLang[0]['sub_bank'] ?? '';
$playerRechargeRecord->owner = $channelRechargeSetting->channel_recharge_method->methodLang[0]['owner'] ?? '';
$playerRechargeRecord->account = $channelRechargeSetting->channel_recharge_method->account;
$playerRechargeRecord->chip_amount = bcmul($allCoins, $channelRechargeSetting->chip_multiple, 2);
$playerRechargeRecord->save();
} catch (Exception $e) {
return jsonFailResponse($e->getMessage());
}
sendSocketMessage('private-admin_group-channel-' . request()->department_id, [
'msg_type' => 'player_examine_recharge_order',
'id' => $rechargeRecord->id,
'player_id' => $player->id,
'player_name' => $player->name,
'player_phone' => $player->phone,
'money' => $playerRechargeRecord->money,
'status' => $playerRechargeRecord->status,
'tradeno' => $playerRechargeRecord->tradeno,
]);
return jsonSuccessResponse('success', [
'tradeno' => $playerRechargeRecord->tradeno,
'order_id' => $playerRechargeRecord->id,
'money' => $playerRechargeRecord->money,
'coins' => $allCoins,
'currency' => $playerRechargeRecord->currency,
'status' => $playerRechargeRecord->status,
'created_at' => strtotime($playerRechargeRecord->created_at),
'recharge_setting' => $channelRechargeSetting
]);
}
/**
* 提现记录
* @param Request $request
* @return Response
* @throws PlayerCheckException|GameException
*/
public function cashOutList(Request $request): Response
{
$player = checkPlayer();
$data = $request->all();
$validator = v::key('page', v::intVal()->setName(trans('page', [], 'message')))
->key('size', v::intVal()->setName(trans('size', [], 'message')));
try {
$validator->assert($data);
} catch (AllOfException $e) {
return jsonFailResponse(getValidationMessages($e));
}
$size = $data['size'] ?? 10;
$totalRecords = PlayerWithdrawRecord::query()
->where('player_id', $player->id)
->count();
return jsonSuccessResponse('success', [
'list' => PlayerWithdrawRecord::where('player_id', $player->id)
->select(['id', 'coins', 'after_coins', 'created_at', 'status'])
->forPage($data['page'] ?? 1, $data['size'] ?? 10)
->orderBy('created_at', 'desc')
->get()
->toArray(),
'total_page' => ceil($totalRecords / $size)
]);
}
/**
* 玩家提现
* @param Request $request
* @return Response
* @throws PlayerCheckException
* @throws Exception
*/
public function playerWithdrawal(Request $request): Response
{
$player = checkPlayer();
$data = $request->all();
$validator = v::key('amount', v::intVal()->notEmpty()->min(100)->setName(trans('withdrawal_amount', [], 'message')))
->key('bank_id', v::intVal()->setName(trans('withdrawal_bank', [], 'message')));
try {
$validator->assert($data);
} catch (AllOfException $e) {
return jsonFailResponse(getValidationMessages($e));
}
/** @var Channel $channel */
$channel = Channel::where('department_id', $player->department_id)->first();
if ($player->status_withdraw != 1) {
return jsonFailResponse(trans('player_withdraw_closed', [], 'message'));
}
if ($player->wallet->money < $data['amount']) {
return jsonFailResponse(trans('insufficient_balance', [], 'message'));
}
if ($channel->withdraw_status == 0) {
return jsonFailResponse(trans('self_withdraw_closed', [], 'message'));
}
if (empty($data['bank_id'])) {
return jsonFailResponse(trans('please_select_player_bank', [], 'message'));
}
/** @var Currency $currency */
$currency = Currency::where('identifying', $channel->currency)->where('status', 1)->whereNull('deleted_at')->first();
if (empty($currency)) {
return jsonFailResponse(trans('currency_no_setting', [], 'message'));
}
/** @var PlayerBank $playerBank */
$playerBank = PlayerBank::where('id', $data['bank_id'])->where('player_id', $player->id)->where('status', 1)->whereNull('deleted_at')->first();
if (empty($playerBank)) {
return jsonFailResponse(trans('player_bank_not_found', [], 'message'));
}
if ($player->must_chip_amount > $player->chip_amount) {
return jsonFailResponse(trans('must_chip_amount_incomplete', [], 'message'));
}
DB::beginTransaction();
try {
$money = bcdiv($data['amount'], $currency->ratio, 2);
// 生成订单
$playerWithdrawRecord = new PlayerWithdrawRecord();
$beforeGameAmount = $player->wallet->money;
// 玩家钱包扣减
$player->wallet->money = bcsub($player->wallet->money, $data['amount'], 2);
$playerWithdrawRecord->player_id = $player->id;
$playerWithdrawRecord->department_id = $player->department_id;
$playerWithdrawRecord->tradeno = createOrderNo();
$playerWithdrawRecord->player_name = $player->name ?? '';
$playerWithdrawRecord->player_phone = $player->phone ?? '';
$playerWithdrawRecord->money = $money;
$playerWithdrawRecord->coins = $data['amount'];
$playerWithdrawRecord->after_coins = $player->wallet->money;
$playerWithdrawRecord->fee = 0;
$playerWithdrawRecord->inmoney = bcsub($playerWithdrawRecord->money, $playerWithdrawRecord->fee, 2); // 实际提现金额
$playerWithdrawRecord->currency = $channel->currency;
$playerWithdrawRecord->bank_name = $playerBank->bank_name;
$playerWithdrawRecord->account = $playerBank->account;
$playerWithdrawRecord->account_name = $playerBank->account_name;
$playerWithdrawRecord->type = PlayerWithdrawRecord::TYPE_SELF;
$playerWithdrawRecord->status = PlayerWithdrawRecord::STATUS_WAIT;
$playerWithdrawRecord->save();
// 更新玩家统计
$player->player_extend->withdraw_amount = bcadd($player->player_extend->withdraw_amount, $playerWithdrawRecord->coins, 2);
$player->push();
//寫入金流明細
$playerDeliveryRecord = new PlayerDeliveryRecord;
$playerDeliveryRecord->player_id = $playerWithdrawRecord->player_id;
$playerDeliveryRecord->department_id = $playerWithdrawRecord->department_id;
$playerDeliveryRecord->target = $playerWithdrawRecord->getTable();
$playerDeliveryRecord->target_id = $playerWithdrawRecord->id;
$playerDeliveryRecord->type = PlayerDeliveryRecord::TYPE_WITHDRAWAL;
$playerDeliveryRecord->source = 'channel_withdrawal';
$playerDeliveryRecord->amount = $playerWithdrawRecord->coins;
$playerDeliveryRecord->amount_before = $beforeGameAmount;
$playerDeliveryRecord->amount_after = $player->wallet->money;
$playerDeliveryRecord->tradeno = $playerWithdrawRecord->tradeno ?? '';
$playerDeliveryRecord->remark = $playerWithdrawRecord->remark ?? '';
$playerDeliveryRecord->save();
DB::commit();
} catch (\Exception $e) {
DB::rollBack();
return jsonFailResponse(trans('system_error', [], 'message') . $e->getMessage());
}
$notice = new Notice();
$notice->department_id = $playerWithdrawRecord->department_id;
$notice->player_id = $playerWithdrawRecord->player_id;
$notice->source_id = $playerWithdrawRecord->id;
$notice->type = Notice::TYPE_EXAMINE_WITHDRAW;
$notice->receiver = Notice::RECEIVER_DEPARTMENT;
$notice->is_private = 0;
$notice->title = '渠道提现待审核';
$notice->content = '提现订单待审核,玩家' . (empty($playerWithdrawRecord->player_name) ? $playerWithdrawRecord->player_name : $playerWithdrawRecord->player_phone) . ', 提现游戏点: ' . $playerWithdrawRecord->point . ' 提现金额: ' . $playerWithdrawRecord->money;
$notice->save();
if ($playerWithdrawRecord->status == PlayerWithdrawRecord::STATUS_FAIL) {
return jsonFailResponse(trans('withdraw_fail', [], 'message'));
}
sendSocketMessage('private-admin_group-channel-' . $channel->department_id, [
'msg_type' => 'player_create_withdraw_order',
'id' => $playerWithdrawRecord->id,
'player_id' => $player->id,
'player_name' => $player->name,
'player_phone' => $player->phone,
'money' => $playerWithdrawRecord->money,
'status' => $playerWithdrawRecord->status,
'tradeno' => $playerWithdrawRecord->tradeno,
]);
return jsonSuccessResponse('success', [
'tradeno' => $playerWithdrawRecord->tradeno,
'order_id' => $playerWithdrawRecord->id,
'money' => $playerWithdrawRecord->money,
'currency' => $playerWithdrawRecord->currency,
'status' => $playerWithdrawRecord->status,
]);
}
/**
* 首页活动
* @return Response
* @throws PlayerCheckException|GameException
*/
public function homeActivity(): Response
{
$player = checkPlayer();
$list = Activity::query()
->where('department_id', $player->department_id)
->where('status', 1)
->limit(3)
->orderBy('sort', 'desc')
->get();
$lang = locale();
$lang = Str::replace('_', '-', $lang);
$activityList = [];
/** @var Activity $activity */
foreach ($list as $activity) {
/** @var ActivityContent $activityContent */
$activityContent = $activity->activity_content->where('lang', $lang)->first();
if ($activity->type == Activity::TYPE_CUSTOM) {
if (strtotime($activity->start_time) > time() || strtotime($activity->end_time) < time()) {
continue;
}
}
if ($activity->type == Activity::TYPE_CYCLE) {
if ($activity->cycle_type == 'Week' && $activity->cycle_data != Carbon::now()->dayOfWeek) {
continue;
}
if ($activity->cycle_type == 'Month' && $activity->cycle_data != Carbon::now()->day) {
continue;
}
}
$activityList[] = [
'id' => $activity->id,
'start_time' => $activity->start_time,
'end_time' => $activity->end_time,
'name' => $activityContent->name ?? '',
'lang' => $activityContent->lang,
'picture' => $activityContent->picture ?? '',
'recharge_id' => $activity->recharge_id,
'method_id' => $activity->channelRechargeSetting->method_id,
];
}
return jsonSuccessResponse('success', [
'list' => $activityList,
]);
}
/**
* cash in记录
* @param Request $request
* @return Response
* @throws PlayerCheckException|GameException
*/
public function cashInList(Request $request): Response
{
$player = checkPlayer();
$data = $request->all();
$validator = v::key('page', v::intVal()->setName(trans('page', [], 'message')))
->key('size', v::intVal()->setName(trans('size', [], 'message')));
try {
$validator->assert($data);
} catch (AllOfException $e) {
return jsonFailResponse(getValidationMessages($e));
}
$size = $data['size'] ?? 10;
$totalRecords = PlayerRechargeRecord::query()
->where('player_id', $player->id)
->count();
$list = PlayerRechargeRecord::leftJoin('player_delivery_record as pdr',function ($join){
$join->on( 'pdr.target_id', '=', 'player_recharge_record.id')
->where('pdr.type', '=', PlayerDeliveryRecord::TYPE_RECHARGE);
})->where('player_recharge_record.player_id', $player->id)
->select(['player_recharge_record.id', 'player_recharge_record.status', 'player_recharge_record.coins', 'pdr.amount_after', 'player_recharge_record.created_at'])
->forPage($data['page'] ?? 1, $data['size'] ?? 10)
->orderBy('player_recharge_record.created_at', 'desc')
->get()
->toArray();
foreach ($list as &$item) {
$item['created_at'] = Carbon::parse($item['created_at'])->format('Y/m/d H:i');
$item['amount_after'] = round($item['amount_after'], 2);
}
return jsonSuccessResponse('success', [
'list' => $list,
'total_page' => ceil($totalRecords / $size)
]);
}
/**
* 消息列表
* @param Request $request
* @return Response
* @throws PlayerCheckException|GameException
*/
public function noticeList(Request $request): Response
{
$player = checkPlayer();
$data = $request->all();
$validator = v::key('page', v::intVal()->setName(trans('page', [], 'message')))
->key('size', v::intVal()->setName(trans('size', [], 'message')));
try {
$validator->assert($data);
} catch (AllOfException $e) {
return jsonFailResponse(getValidationMessages($e));
}
$noticeList = Notice::query()
->leftJoin('player_withdraw_record', 'player_withdraw_record.id', '=', 'notice.source_id')
->select(['notice.id', 'notice.player_id', 'notice.title', 'notice.type', 'notice.content', 'notice.created_at', 'player_withdraw_record.status', 'player_withdraw_record.money'])
->where('notice.player_id', $player->id)
->where('notice.receiver', Notice::RECEIVER_PLAYER)
->where('notice.is_private', 1)
->whereNull('notice.deleted_at')
->forPage($data['page'], $data['size'])
->orderBy('notice.status', 'asc')
->orderBy('notice.id', 'desc')
->get();
foreach ($noticeList as &$item) {
$item['title'] = trans('title.' . $item->type.'.'.$item->status,
['{point}' => $item->money],
'notice');
$item['content'] = trans('content.' . $item->type.'.'.$item->status,
['{point}' => $item->money],
'notice');
}
// 更新为已读状态
Notice::where('status', 0)
->where('receiver', Notice::RECEIVER_PLAYER)
->where('is_private', 1)
->where('player_id', $player->id)
->whereNull('deleted_at')
->update([
'status' => 1
]);
return jsonSuccessResponse('success', [
'list' => $noticeList
]);
}
/**
* 渠道信息
* @return Response
* @throws PlayerCheckException|GameException
*/
public function channelInfo(): Response
{
$player = checkPlayer();
return jsonSuccessResponse('success', [
'channel_info' => Channel::query()
->where('department_id', $player->department_id)
->first(),
]);
}
/**
* 提现记录
* @param Request $request
* @return Response
* @throws PlayerCheckException|GameException
*/
public function withdrawList(Request $request): Response
{
checkPlayer();
$date = date('Y-m-d');
$list = PlayerWithdrawRecord::with('player:id,uuid,phone')
->where('status', 2)
->whereDate('finish_time', $date)
->select(['money', 'player_id', 'updated_at'])
->orderBy('finish_time', 'desc')
->get()
->toArray();
foreach ($list as &$item) {
$PlayerWalletTransfer = PlayerWalletTransfer::query()->where('player_id', $item['player_id'])
->where('type', PlayerWalletTransfer::TYPE_IN)
->where('created_at', '<=', $item['updated_at'])
->orderBy('id', 'desc')
->first();
$item['game'] = $PlayerWalletTransfer->gamePlatform->title;
}
unset($item);
$copyList = [];
$broadcast = Broadcast::query()
->select('title','num','phone','updated_at')
->where('status', 1)
->where('type', 1)
->wheredate('date', $date)
->get()
->toArray();
foreach ($broadcast as $item) {
$copyList[] = [
'money' => $item['num'],
'player_id' => 0,
'updated_at' => $item['updated_at'],
'player' => [
'id' => 0,
'uuid' => 0,
'phone' => $item['phone'],
],
'game' => $item['title']
];
}
$data = array_merge($list, $copyList);
$key = array_column($data,'updated_at');
array_multisort($key, SORT_DESC, $data);
return jsonSuccessResponse('success', [
'list' => $data,
]);
}
}

View File

@@ -0,0 +1,1251 @@
<?php
namespace app\api\controller\v1;
use addons\webman\model\BankList;
use addons\webman\model\Channel;
use addons\webman\model\ChannelRechargeMethod;
use addons\webman\model\ChannelRechargeSetting;
use addons\webman\model\Currency;
use addons\webman\model\DrawRecord;
use addons\webman\model\Game;
use addons\webman\model\PlayerRechargeRecord;
use addons\webman\model\Notice;
use addons\webman\model\Player;
use addons\webman\model\PlayerBank;
use addons\webman\model\PlayerDeliveryRecord;
use addons\webman\model\PlayerWithdrawRecord;
use addons\webman\model\Prize;
use addons\webman\model\SepayRecharge;
use app\exception\PromoterCheckException;
use app\exception\GameException;
use app\exception\PlayerCheckException;
use app\service\SklPayServices;
use Illuminate\Support\Carbon;
use Illuminate\Support\Str;
use Respect\Validation\Exceptions\AllOfException;
use Respect\Validation\Validator as v;
use support\Db;
use support\Request;
use support\Response;
use think\Exception;
use Tinywan\Jwt\JwtToken;
use addons\webman\model\PhoneSmsLog;
use support\Redis;
use Webman\Push\PushException;
class PlayerController
{
/** 排除验签 */
protected $noNeedSign = [];
/**
* 登录接口
* @param Request $request
* @return Response
* @throws \Exception
*/
public function login(Request $request): Response
{
$validator = v::key('phone', v::stringType()->notEmpty()->setName(trans('phone', [], 'message')))
->key('type', v::in([1, 2])->notEmpty()->setName(trans('type', [], 'message')))
->key('code', v::stringType()->setName(trans('code', [], 'message')), false)
->key('password', v::stringType()->setName(trans('password', [], 'message')), false);
$data = $request->all();
try {
$validator->assert($data);
//手机号规则
if(!validateMalaysianPhoneNumber($data['phone'])){
throw new Exception(trans('phone_val_error', [], 'message'));
}
$pattern = '/(\+60|60|0060)01\d{8,9}/';
if (preg_match($pattern, $data['phone'])) {
$data['phone'] = substr_replace($data['phone'], '601', 0, 4);
}
/** @var Player $player */
$player = Player::where('phone', $data['phone'])
->where('department_id', request()->department_id)
->first();
if(empty($player)){
throw new Exception(trans('player_not_found', [], 'message'));
}
//密码登录
if($data['type'] == 1){
if(!isset($data['password']) || !password_verify($data['password'],$player->password)){
throw new Exception(trans('password_error', [], 'message'));
}
}else{
//验证码错误
$phone_code = Redis::get(setSmsKey($data['phone'], PhoneSmsLog::TYPE_LOGIN));
if(!$phone_code){
throw new Exception(trans('phone_sms_expire', [], 'message'));
}
if(!isset($data['code']) || empty($data['code']) || $phone_code != $data['code']){
throw new Exception(trans('phone_sms_error', [], 'message'));
}
}
if ($player->status == Player::STATUS_STOP) {
throw new Exception(trans('account_stop', [], 'message'));
}
$player->last_login = date('Y-m-d H:i:s');
addLoginRecord($player->id);
} catch (AllOfException $e) {
throw new Exception(getValidationMessages($e));
} catch (Exception $e) {
throw new Exception($e->getMessage());
}
return jsonSuccessResponse('success', [
'token' => JwtToken::generateToken([
'id' => $player->id,
'avatar' => $player->avatar,
'name' => $player->name,
'type' => $player->type,
'currency' => $player->currency,
'recommended_code' => $player->recommended_code,
]),
]);
}
/**
* 手机号注册
* @param Request $request
* @return Response
* @throws Exception
*/
public function phoneRegister(Request $request): Response
{
$data = $request->all();
$validator = v::key('phone', v::stringType()->notEmpty()->setName(trans('phone', [], 'message')))
->key('code', v::stringType()->setName(trans('code', [], 'message')))
->key('password', v::stringType()->setName(trans('password', [], 'message')))
->key('confim_password', v::stringType()->setName(trans('confim_password', [], 'message')))
->key('nickname', v::stringType()->setName(trans('nickname', [], 'message')));
try {
$validator->assert($data);
//网页注册
if(!empty($data['web_register']) && empty($data['recommended_code'])){
throw new Exception(trans('recommend_player_not_found', [], 'message'));
}
//手机号规则
if(!validateMalaysianPhoneNumber($data['phone'])){
throw new Exception(trans('phone_val_error', [], 'message'));
}
$pattern = '/(\+60|60|0060)01\d{8,9}/';
if (preg_match($pattern, $data['phone'])) {
$data['phone'] = substr_replace($data['phone'], '601', 0, 4);
}
//两次密码不一致
if($data['password'] != $data['confim_password']){
throw new Exception(trans('confim_password_differ', [], 'message'));
}
//验证码错误
$phone_code = Redis::get(setSmsKey($data['phone'], PhoneSmsLog::TYPE_REGISTER));
if(!$phone_code){
throw new Exception(trans('phone_sms_expire', [], 'message'));
}
if(!isset($data['code']) || empty($data['code']) || $phone_code != $data['code']){
throw new Exception(trans('phone_sms_error', [], 'message'));
}
/** @var Player $player */
$player = Player::where('phone', $data['phone'])
->where('department_id', request()->department_id)
->first();
//手机号已被注册
if($player){
throw new Exception(trans('phone_has_registered', [], 'message'));
}
$create_player['phone'] = $data['phone'];
$create_player['password'] = $data['password'];
$create_player['name'] = $data['nickname'];
$player = createPlayer(request()->department_id,$create_player);
} catch (AllOfException $e) {
throw new Exception(getValidationMessages($e));
} catch (Exception $e) {
throw new Exception(trans('create_player_fail', [], 'message') . $e->getMessage());
}
return jsonSuccessResponse('success', [
'token' => JwtToken::generateToken([
'id' => $player->id,
'avatar' => $player->avatar,
'name' => $player->name,
'type' => $player->type,
'currency' => $player->currency,
'recommended_code' => $player->recommended_code,
]),
]);
}
/**
* 忘记密码
* @param Request $request
* @return Response
* @throws \Exception
*/
public function forgetPassword(Request $request): Response
{
$data = $request->all();
$validator = v::key('phone', v::stringType()->notEmpty()->setName(trans('phone', [], 'message')))
->key('code', v::stringType()->setName(trans('code', [], 'message')))
->key('password', v::stringType()->setName(trans('password', [], 'message')))
->key('confim_password', v::stringType()->setName(trans('confim_password', [], 'message')));
try {
$validator->assert($data);
//手机号规则
if(!validateMalaysianPhoneNumber($data['phone'])){
throw new Exception(trans('phone_val_error', [], 'message'));
}
$pattern = '/(\+60|60|0060)01\d{8,9}/';
if (preg_match($pattern, $data['phone'])) {
$data['phone'] = substr_replace($data['phone'], '601', 0, 4);
}
//两次密码不一致
if($data['password'] != $data['confim_password']){
throw new Exception(trans('confim_password_differ', [], 'message'));
}
//验证码错误
$phone_code = Redis::get(setSmsKey($data['phone'], PhoneSmsLog::TYPE_CHANGE_PASSWORD));
if(!$phone_code){
throw new Exception(trans('phone_sms_expire', [], 'message'));
}
if(!isset($data['code']) || empty($data['code']) || $phone_code != $data['code']){
throw new Exception(trans('phone_sms_error', [], 'message'));
}
/** @var Player $player */
$player = Player::where('phone', $data['phone'])
->where('department_id', request()->department_id)
->first();
if(empty($player)){
throw new Exception(trans('player_not_found', [], 'message'));
}
$player->password = $data['password'];
$player->push();
} catch (AllOfException $e) {
throw new Exception(getValidationMessages($e));
} catch (Exception $e) {
throw new Exception($e->getMessage());
}
return jsonSuccessResponse(trans('password_revise_suc', [], 'message'));
}
/**
* 发送验证码
* @param Request $request
* @return Response
* @throws \Exception
*/
public function sendMsg(Request $request): Response
{
$data = $request->all();
$validator = v::key('phone', v::stringType()->notEmpty()->setName(trans('phone', [], 'message')))
->key('type', v::stringType()->setName(trans('type', [], 'message')), false);
try {
$validator->assert($data);
//手机号规则
if(!validateMalaysianPhoneNumber($data['phone'])){
throw new Exception(trans('phone_val_error', [], 'message'));
}
$pattern = '/(\+60|60|0060)01\d{8,9}/';
if (preg_match($pattern, $data['phone'])) {
$data['phone'] = substr_replace($data['phone'], '601', 0, 4);
}
if ($data['type'] == 1 || $data['type'] == 3) {
$player = Player::query()->where('phone', $data['phone'])->first();
if(empty($player)){
throw new Exception(trans('player_not_found', [], 'message'));
}
}
$env = config('app.env');
$config = config('sms');
$code = ($env == 'pro' ? random_int(10000, 99999) : config('sms.default_code'));
//发送验证码
$params = [
"userId" => $config['userId'],
"account"=> $config['account'],
"password"=> $config['password'],
"mobile"=> $data['phone'],
"content"=> '【Samsung88】您的验证码为'.$code.'5分钟内有效。',
"sendTime"=> "",
"action"=> "sendhy",
"custom"=> "1"
];
$result = doCurl($config['aliwy_url'], $params);
if($result['code'] == 200 && $result['data']['status'] == 'Success'){
$key = setSmsKey($data['phone'], $data['type']);
$phoneSmsLog = new PhoneSmsLog();
$phoneSmsLog->player_id = isset($data['player_id']) ? $data['player_id'] : 0;
$phoneSmsLog->code = $code;
$phoneSmsLog->phone = $data['phone'];
$phoneSmsLog->send_times = 1;
$phoneSmsLog->type = $data['type'];
$phoneSmsLog->expire_time = date("Y-m-d H:i:s", time() + 300);
$phoneSmsLog->response = $result ? json_encode($result) : '';
Redis::set($key, $code, 'EX', 300);
$phoneSmsLog->status = 1;
$phoneSmsLog->save();
}else{
throw new Exception($result['data']['message']);
}
} catch (AllOfException $e) {
throw new Exception(getValidationMessages($e));
}catch (Exception $e) {
throw new Exception($e->getMessage());
}
return jsonSuccessResponse(trans('phone_sms_send_success', [], 'message'));
}
/**
* 获取用户信息
* @param Request $request
* @return Response
* @throws GameException
* @throws PlayerCheckException
*/
public function playerInfo(Request $request): Response
{
$player = checkPlayer();
$prizes = Prize::query()
->select('pic', 'name', 'id', 'type')
->where('department_id', $player->department_id)
->where('status', 1)
->orderBy('probability')
->get()
->toArray();
$game = Game::query()->select('description', 'game_url')->first();
return jsonSuccessResponse('success', [
'id' => $player->id,
'phone' => $player->phone,
'avatar' => $player->avatar,
'currency' => $player->currency,
'uuid' => $player->uuid,
'money' => $player->wallet->money,
'name' => $player->name,
'recharge_amount' => $player->player_extend->recharge_amount ?? 0,
'game_description' => $game->description,
'game_url' => $game->game_url,
'prize_list' => $prizes,
'bank_list' => PlayerBank::query()
->select('id', 'bank_name', 'account', 'account_name', 'bank_code')
->where('player_id', $player->id)
->where('status', 1)
->where('pay_type', '!=', 4)
->get(),
'usdt' => PlayerBank::query()
->select('id', 'wallet_address', 'qr_code')
->where('player_id', $player->id)
->where('status', 1)
->where('pay_type', 4)
->first(),
'whats_app' => Channel::query()->where('department_id', $player->department_id)->value('telegram_url'),
]);
}
/**
* 获取充值列表
* @return Response
* @throws PlayerCheckException|GameException
*/
public function rechargeList(): Response
{
$player = checkPlayer();
$lang = locale();
$lang = Str::replace('_', '-', $lang);
$channelRechargeMethod = ChannelRechargeMethod::query()
->with(['methodLang' => function ($query) use ($lang) {
$query->select(['id', 'bank_name', 'sub_bank', 'owner', 'name', 'method_id', 'lang'])->where('lang', $lang);
}])
->where('status', 1)
->where('department_id', $player->department_id)
->orderBy('created_at', 'desc')
->select(['id', 'account', 'currency'])
->get();
$list = [];
/** @var ChannelRechargeMethod $item */
foreach ($channelRechargeMethod as $item) {
$list[] = [
'id' => $item->id,
'account' => $item->account,
'currency' => $item->currency,
'name' => $item->methodLang[0]['name'] ?? '',
'bank_name' => $item->methodLang[0]['bank_name'] ?? '',
'sub_bank' => $item->methodLang[0]['sub_bank'] ?? '',
'owner' => $item->methodLang[0]['sub_bank'] ?? '',
];
}
return jsonSuccessResponse('success', [
'list' => $list,
]);
}
/**
* 获取充值项列表
* @param Request $request
* @return Response
* @throws PlayerCheckException|GameException
*/
public function rechargeSettingList(Request $request): Response
{
$player = checkPlayer();
$data = $request->all();
$validator = v::key('method_id', v::oneOf(v::intVal(), v::equals(''))->setName(trans('method_id', [], 'message')), false)
->key('setting_id', v::oneOf(v::intVal(), v::equals(''))->setName(trans('setting_id', [], 'message')), false)
->key('type', v::in([ChannelRechargeSetting::TYPE_REGULAR, ChannelRechargeSetting::TYPE_ACTIVITY, ''])->setName(trans('recharge_type', [], 'message')), false);
try {
$validator->assert($data);
} catch (AllOfException $e) {
return jsonFailResponse(getValidationMessages($e));
}
$list = ChannelRechargeSetting::query()
->whereHas('channel_recharge_method', function ($query) {
$query->where('status', 1);
})
->when(!empty($data['method_id']), function ($query) use ($data) {
$query->where('method_id', $data['method_id']);
})
->when(!empty($data['setting_id']), function ($query) use ($data) {
$query->where('id', $data['setting_id']);
})
->when(!empty($data['type']), function ($query) use ($data) {
$query->where('type', $data['type']);
})
->where('department_id', $player->department_id)
->where('status', 1)
->orderBy('coins_num', 'asc')
->select(['id', 'title', 'chip_multiple', 'coins_num', 'money', 'type', 'method_id'])
->get();
return jsonSuccessResponse('success', [
'list' => $list,
]);
}
/**
* 充值详情
* @param Request $request
* @return Response
* @throws PlayerCheckException|GameException
*/
public function rechargeInfo(Request $request): Response
{
$player = checkPlayer();
$data = $request->all();
$validator = v::key('id', v::intVal()->notEmpty()->setName(trans('recharge_id', [], 'message')));
try {
$validator->assert($data);
} catch (AllOfException $e) {
return jsonFailResponse(getValidationMessages($e));
}
$lang = locale();
$lang = Str::replace('_', '-', $lang);
/** @var ChannelRechargeSetting $recharge */
$recharge = ChannelRechargeSetting::query()
->with(['channel_recharge_method' => function ($query) use ($lang) {
$query->select(['id', 'account', 'currency'])->with(['methodLang' => function ($query) use ($lang) {
$query->select(['id', 'bank_name', 'sub_bank', 'owner', 'name', 'method_id', 'lang'])->where('lang', $lang);
}]);
}])
->where('department_id', $player->department_id)
->where('id', $data['id'])
->select(['id', 'title', 'chip_multiple', 'coins_num', 'gift_coins', 'money', 'type', 'method_id', 'status'])
->first();
if (!$recharge) {
return jsonFailResponse(trans('recharge_setting_not_found', [], 'message'));
}
if ($recharge->status == 0) {
return jsonFailResponse(trans('recharge_setting_disabled', [], 'message'));
}
return jsonSuccessResponse(trans('success', [], 'message'), [
'recharge_setting' => $recharge
]);
}
/**
* 编辑玩家名称
* @param Request $request
* @return Response
* @throws PlayerCheckException|GameException
*/
public function editPlayerName(Request $request): Response
{
$player = checkPlayer();
$data = $request->all();
$validator = v::key('player_name', v::stringVal()->length(1, 50)->setName(trans('player_name', [], 'message')));
try {
$validator->assert($data);
} catch (AllOfException $e) {
return jsonFailResponse(getValidationMessages($e));
}
if (Player::withTrashed()->where('name', $data['player_name'])->where('id', '!=', $player->id)->exists()) {
return jsonFailResponse(trans('player_name_has_exist', [], 'message'));
}
$player->name = $data['player_name'] ?? '';
$player->save();
return jsonSuccessResponse('success');
}
/**
* 數據中心
* @return Response
* @throws PlayerCheckException|GameException
*/
public function giftCenter(): Response
{
$player = checkPlayer();
return jsonSuccessResponse('success', [
'chip_amount' => $player->chip_amount,
'must_chip_amount' => $player->must_chip_amount,
'coins' => $player->wallet->money,
]);
}
/**
* 获取四方充值列表
* @return Response
* @throws PlayerCheckException|GameException
*/
public function seRechargeList(): Response
{
$player = checkPlayer(false);
$list = SepayRecharge::query()->where('department_id', $player->department_id)
->where('status', 1)
->orderBy('money')
->select(['id', 'title', 'coins_num', 'gift_coins', 'first_coins', 'money'])
->get()->toArray();
$time = date('H:i:s');
foreach ($list as &$value){
$value['P2PDEPOSIT'] = 0;
$value['DUITNOWP2P'] = 0;
if ($value['money'] >= 5 && $value['money'] <= 1000) {
$value['P2PDEPOSIT'] = 1;
if ($time >= '23:50:00' || $time <= '01:05:00') {
$value['P2PDEPOSIT'] = 0;
}
}
if ($value['money'] >= 30 && $value['money'] <= 20000) {
$value['DUITNOWP2P'] = 1;
}
$firstRecharge = PlayerRechargeRecord::query()
->where('player_id', $player->id)
->where('status', 2)
->where('setting_id', $value['id'])
->first();
if ($firstRecharge) {
$value['first_coins'] = '0.00';
}
}
unset($value);
$bankList = [];
$bankSelect = 0;
if (in_array($player->channel->pay_type, [2, 3])) {
$bankSelect = 1;
$bankList = BankList::query()
->select(['bank_name', 'bank_code'])
->where('pay_type', $player->channel->pay_type)
->where('type', 1)
->whereNull('deleted_at')
->get()
->toArray();
}
$rechargeSetting = ChannelRechargeMethod::query()
->with('methodLang')
->select(['id', 'account', 'wallet_address', 'qr_code', 'type'])
->where('department_id', $player->department_id)
->where('status', 1)
->get()->toArray();
foreach ($rechargeSetting as &$item) {
if ($item['type'] == 1) {
$item['name'] = 'artificial';
} else {
$item['name'] = 'usdt';
}
}
unset($item);
$rechargeSetting[] = [
'name' => 'SKL',
'type' => 3,
];
return jsonSuccessResponse('success', [
'list' => $list,
'bank_select' => $bankSelect,
'bank_list' => $bankList,
'recharge_setting' => $rechargeSetting
]);
}
/**
* 玩家充值
* @param Request $request
* @return Response
* @throws PlayerCheckException
* @throws GameException
*/
public function seRecharge(Request $request): Response
{
$player = checkPlayer();
$data = $request->all();
$validator = v::key('amount', v::notEmpty()->intVal()->setName(trans('recharge_amount', [], 'message')))
->key('type', v::notEmpty()->intVal()->setName(trans('type', [], 'message')));
try {
$validator->assert($data);
} catch (AllOfException $e) {
return jsonFailResponse(getValidationMessages($e));
}
/** @var Channel $channel */
$channel = Channel::where('department_id', $player->department_id)->first();
if (empty($channel)) {
return jsonFailResponse(trans('channel_not_found', [], 'message'));
}
/** @var SepayRecharge $sepayInfo */
$sepayInfo = SepayRecharge::query()->where('department_id', $player->department_id)
->where('money', $data['amount'])->first();
$firstCoins = 0;
$allCoins = $data['amount'];
if(!empty($sepayInfo)) {
if ($sepayInfo->first_coins > 0) {
$firstRecharge = PlayerRechargeRecord::query()
->where('player_id', $player->id)
->where('status', 2)
->where('setting_id', $sepayInfo->id)
->doesntExist();
if ($firstRecharge) {
$firstCoins = $sepayInfo->first_coins;
}
}
$allCoins = bcadd($sepayInfo->coins_num, $firstCoins, 2);
}
$orderNo = createOrderNo();
// 生成充值订单
$playerRechargeRecord = new PlayerRechargeRecord();
$playerRechargeRecord->player_id = $player->id;
$playerRechargeRecord->department_id = $player->department_id;
$playerRechargeRecord->tradeno = $orderNo;
$playerRechargeRecord->player_name = $player->name ?? '';
$playerRechargeRecord->money = $data['amount'];
$playerRechargeRecord->inmoney = $data['amount'];
$playerRechargeRecord->setting_id = $sepayInfo->id ?? 0;
$playerRechargeRecord->coins = $data['amount'];
$playerRechargeRecord->gift_coins = $firstCoins;
$playerRechargeRecord->currency = 'RM';
//手动支付
if ($data['type'] == 1) {
$playerRechargeRecord->type = PlayerRechargeRecord::TYPE_REGULAR;
$playerRechargeRecord->status = PlayerRechargeRecord::STATUS_WAIT;
$playerRechargeRecord->save();
//USDT支付
} elseif ($data['type'] == 2) {
$rate = ChannelRechargeMethod::query()
->where('department_id', $player->department_id)
->where('type', 2)
->value('rate');
$playerRechargeRecord->rate = $rate;
$playerRechargeRecord->currency = 'USDT';
$playerRechargeRecord->type = PlayerRechargeRecord::TYPE_REGULAR;
$playerRechargeRecord->status = PlayerRechargeRecord::STATUS_WAIT;
$playerRechargeRecord->save();
//三方支付
}elseif ($data['type'] == 3) {
if ($data['payment_code'] == 'DUITNOWP2P') {
$paymentCode = 'QR';
} else {
$paymentCode = $data['bank_code'];
}
$params = [
'amount' => $data['amount'],
'paymentCode' => $paymentCode,
'name' => 'ggl-ds ' . $player->uuid,
'orderNo' => $orderNo,
];
$res = (new SklPayServices())->deposit($params);
if (!isset($res['status']) && empty($res['code'])) {
DB::beginTransaction();
try {
$pay_url = $res['transaction_link'];
$playerRechargeRecord->payment_method = $params['paymentCode'] == 'QR' ? 'QR' : 'P2P';
$playerRechargeRecord->external_reference = $res['transaction_id'];
$playerRechargeRecord->type = PlayerRechargeRecord::TYPE_REGULAR;
$playerRechargeRecord->status = PlayerRechargeRecord::STATUS_WAIT;
$playerRechargeRecord->remark = 'SKL99';
$playerRechargeRecord->save();
DB::commit();
} catch (\Exception $e) {
DB::rollBack();
return jsonFailResponse($e->getMessage());
}
return jsonSuccessResponse('success', ['pay_url' => $pay_url]);
} else {
return jsonFailResponse($res['error']);
}
}
sendSocketMessage('private-admin_group-channel-' . request()->department_id, [
'msg_type' => 'player_examine_recharge_order',
'id' => $playerRechargeRecord->id,
'player_id' => $player->id,
'player_name' => $player->name,
'player_phone' => $player->phone,
'money' => $playerRechargeRecord->money,
'status' => $playerRechargeRecord->status,
'tradeno' => $playerRechargeRecord->tradeno,
]);
$rechargeSetting = ChannelRechargeMethod::query()
->with('methodLang')
->select(['id', 'account', 'wallet_address', 'qr_code', 'type'])
->where('department_id', $player->department_id)
->where('status', 1)
->where('type', $data['type'])
->get()->toArray();
return jsonSuccessResponse('success', [
'tradeno' => $playerRechargeRecord->tradeno,
'order_id' => $playerRechargeRecord->id,
'money' => $playerRechargeRecord->money,
'coins' => $allCoins,
'currency' => $playerRechargeRecord->currency,
'status' => $playerRechargeRecord->status,
'created_at' => strtotime($playerRechargeRecord->created_at),
'recharge_setting' => $rechargeSetting
]);
}
/**
* 完成充值
* @param Request $request
* @return Response
* @throws PlayerCheckException
* @throws PushException|GameException
*/
public function completeRecharge(Request $request): Response {
$player = checkPlayer();
$data = $request->post();
$validator = v::key('id', v::notEmpty()->intVal()->setName(trans('recharge_record_id', [], 'message')))
->key('certificate', v::notEmpty()->stringVal()->setName(trans('certificate', [], 'message')));
try {
$validator->assert($data);
} catch (AllOfException $e) {
return jsonFailResponse(getValidationMessages($e));
}
/** @var PlayerRechargeRecord $rechargeRecord */
$rechargeRecord = PlayerRechargeRecord::query()
->where('player_id', $player->id)
->where('id', $data['id'])
->first();
if (empty($rechargeRecord)) {
return jsonFailResponse(trans('recharge_record_not_found', [], 'message'));
}
switch ($rechargeRecord->status) {
case PlayerRechargeRecord::STATUS_RECHARGING:
return jsonFailResponse(trans('recharge_record_review_in_progress', [], 'message'));
case PlayerRechargeRecord::STATUS_RECHARGED_SUCCESS:
return jsonFailResponse(trans('recharge_completed', [], 'message'));
case PlayerRechargeRecord::STATUS_RECHARGED_FAIL:
return jsonFailResponse(trans('recharge_failed', [], 'message'));
case PlayerRechargeRecord::STATUS_RECHARGED_CANCEL:
return jsonFailResponse(trans('player_has_cancelled_recharge', [], 'message'));
case PlayerRechargeRecord::STATUS_RECHARGED_REJECT:
return jsonFailResponse(trans('recharge_order_review_failed', [], 'message'));
case PlayerRechargeRecord::STATUS_RECHARGED_SYSTEM_CANCEL:
return jsonFailResponse(trans('system_cancels_order', [], 'message'));
}
$rechargeRecord->status = PlayerRechargeRecord::STATUS_RECHARGING;
$rechargeRecord->certificate = $data['certificate'];
$rechargeRecord->save();
sendSocketMessage('private-admin_group-channel-' . $player->department_id, [
'msg_type' => 'player_examine_recharge_order',
'id' => $rechargeRecord->id,
'player_id' => $player->id,
'player_name' => $player->name,
'player_phone' => $player->phone,
'money' => $rechargeRecord->money,
'status' => $rechargeRecord->status,
'tradeno' => $rechargeRecord->tradeno,
]);
return jsonSuccessResponse('success');
}
/**
* 玩家提现
* @param Request $request
* @return Response
* @throws PlayerCheckException
* @throws GameException
*/
public function seWithdrawal(Request $request): Response
{
$player = checkPlayer();
$data = $request->all();
$validator = v::key('amount', v::intVal()->notEmpty()->min(100)->max(20000)->setName(trans('withdrawal_amount', [], 'message')))
->key('bank_id', v::intVal()->setName(trans('withdrawal_bank', [], 'message')))
->key('type', v::intVal()->setName(trans('type', [], 'message')));
try {
$validator->assert($data);
} catch (AllOfException $e) {
return jsonFailResponse(getValidationMessages($e));
}
/** @var Channel $channel */
$channel = Channel::where('department_id', $player->department_id)->first();
if ($player->status_withdraw != 1) {
return jsonFailResponse(trans('player_withdraw_closed', [], 'message'));
}
if ($player->wallet->money < $data['amount']) {
return jsonFailResponse(trans('insufficient_balance', [], 'message'));
}
if ($channel->withdraw_status == 0) {
return jsonFailResponse(trans('self_withdraw_closed', [], 'message'));
}
if (empty($data['bank_id'])) {
return jsonFailResponse(trans('please_select_player_bank', [], 'message'));
}
/** @var Currency $currency */
$currency = Currency::where('identifying', $channel->currency)->where('status', 1)->whereNull('deleted_at')->first();
if (empty($currency)) {
return jsonFailResponse(trans('currency_no_setting', [], 'message'));
}
/** @var PlayerBank $playerBank */
$playerBank = PlayerBank::where('id', $data['bank_id'])
->where('player_id', $player->id)
->where('status', 1)
->whereNull('deleted_at')
->first();
if (empty($playerBank)) {
return jsonFailResponse(trans('player_bank_not_found', [], 'message'));
}
//计算汇率
$money = bcdiv($data['amount'], $currency->ratio, 2);
//大于5000需要后台审核
/*if ($money >= 5000) {
DB::beginTransaction();
try {
$money = bcdiv($data['amount'], $currency->ratio, 2);
// 生成订单
$playerWithdrawRecord = new PlayerWithdrawRecord();
$beforeGameAmount = $player->wallet->money;
// 玩家钱包扣减
$player->wallet->money = bcsub($player->wallet->money, $data['amount'], 2);
$playerWithdrawRecord->player_id = $player->id;
$playerWithdrawRecord->department_id = $player->department_id;
$playerWithdrawRecord->tradeno = createOrderNo();
$playerWithdrawRecord->player_name = $player->name ?? '';
$playerWithdrawRecord->player_phone = $player->phone ?? '';
$playerWithdrawRecord->money = $money;
$playerWithdrawRecord->coins = $data['amount'];
$playerWithdrawRecord->after_coins = $player->wallet->money;
$playerWithdrawRecord->fee = 0;
$playerWithdrawRecord->inmoney = bcsub($playerWithdrawRecord->money, $playerWithdrawRecord->fee, 2); // 实际提现金额
$playerWithdrawRecord->currency = $channel->currency;
$playerWithdrawRecord->bank_name = $playerBank->bank_name;
$playerWithdrawRecord->bank_code = $playerBank->bank_code;
$playerWithdrawRecord->account = $playerBank->account;
$playerWithdrawRecord->account_name = $playerBank->account_name;
$playerWithdrawRecord->type = PlayerWithdrawRecord::TYPE_SELF;
$playerWithdrawRecord->status = PlayerWithdrawRecord::STATUS_WAIT;
$playerWithdrawRecord->save();
// 更新玩家统计
$player->player_extend->withdraw_amount = bcadd($player->player_extend->withdraw_amount, $playerWithdrawRecord->coins, 2);
$player->push();
//寫入金流明細
$playerDeliveryRecord = new PlayerDeliveryRecord;
$playerDeliveryRecord->player_id = $playerWithdrawRecord->player_id;
$playerDeliveryRecord->department_id = $playerWithdrawRecord->department_id;
$playerDeliveryRecord->target = $playerWithdrawRecord->getTable();
$playerDeliveryRecord->target_id = $playerWithdrawRecord->id;
$playerDeliveryRecord->type = PlayerDeliveryRecord::TYPE_WITHDRAWAL;
$playerDeliveryRecord->source = 'channel_withdrawal';
$playerDeliveryRecord->amount = $playerWithdrawRecord->coins;
$playerDeliveryRecord->amount_before = $beforeGameAmount;
$playerDeliveryRecord->amount_after = $player->wallet->money;
$playerDeliveryRecord->tradeno = $playerWithdrawRecord->tradeno ?? '';
$playerDeliveryRecord->remark = $playerWithdrawRecord->remark ?? '';
$playerDeliveryRecord->save();
DB::commit();
} catch (\Exception $e) {
DB::rollBack();
return jsonFailResponse(trans('system_error', [], 'message') . $e->getMessage());
}
$notice = new Notice();
$notice->department_id = $playerWithdrawRecord->department_id;
$notice->player_id = $playerWithdrawRecord->player_id;
$notice->source_id = $playerWithdrawRecord->id;
$notice->type = Notice::TYPE_EXAMINE_WITHDRAW;
$notice->receiver = Notice::RECEIVER_DEPARTMENT;
$notice->is_private = 0;
$notice->title = '渠道提现待审核';
$notice->content = '提现订单待审核,玩家' . (empty($playerWithdrawRecord->player_name) ? $playerWithdrawRecord->player_name : $playerWithdrawRecord->player_phone) . ', 提现游戏点: ' . $playerWithdrawRecord->point . ' 提现金额: ' . $playerWithdrawRecord->money;
$notice->save();
if ($playerWithdrawRecord->status == PlayerWithdrawRecord::STATUS_FAIL) {
return jsonFailResponse(trans('withdraw_fail', [], 'message'));
}
sendSocketMessage('private-admin_group-channel-' . $channel->department_id, [
'msg_type' => 'player_create_withdraw_order',
'id' => $playerWithdrawRecord->id,
'player_id' => $player->id,
'player_name' => $player->name,
'player_phone' => $player->phone,
'money' => $playerWithdrawRecord->money,
'status' => $playerWithdrawRecord->status,
'tradeno' => $playerWithdrawRecord->tradeno,
]);
return jsonSuccessResponse('success', [
'tradeno' => $playerWithdrawRecord->tradeno,
'order_id' => $playerWithdrawRecord->id,
'money' => $playerWithdrawRecord->money,
'currency' => $playerWithdrawRecord->currency,
'status' => $playerWithdrawRecord->status,
]);
}*/
// 生成订单
$orderNo = createOrderNo();
$playerWithdrawRecord = new PlayerWithdrawRecord();
$beforeGameAmount = $player->wallet->money;
// 玩家钱包扣减
$player->wallet->money = bcsub($player->wallet->money, $data['amount'], 2);
$playerWithdrawRecord->player_id = $player->id;
$playerWithdrawRecord->department_id = $player->department_id;
$playerWithdrawRecord->tradeno = $orderNo;
$playerWithdrawRecord->player_name = $player->name ?? '';
$playerWithdrawRecord->player_phone = $player->phone ?? '';
$playerWithdrawRecord->money = $money;
$playerWithdrawRecord->coins = $data['amount'];
$playerWithdrawRecord->after_coins = $player->wallet->money;
$playerWithdrawRecord->fee = 0;
$playerWithdrawRecord->inmoney = bcsub($playerWithdrawRecord->money, $playerWithdrawRecord->fee, 2); // 实际提现金额
$playerWithdrawRecord->currency = $channel->currency;
DB::beginTransaction();
try {
//银行卡提现
if ($data['type'] == 1) {
$playerWithdrawRecord->bank_name = $playerBank->bank_name;
$playerWithdrawRecord->bank_code = $playerBank->bank_code;
$playerWithdrawRecord->account = $playerBank->account;
$playerWithdrawRecord->account_name = $playerBank->account_name;
$playerWithdrawRecord->type = PlayerWithdrawRecord::TYPE_SELF;
$playerWithdrawRecord->status = PlayerWithdrawRecord::STATUS_WAIT;
$playerWithdrawRecord->save();
//USDT提现
} elseif ($data['type'] == 2) {
$rate = ChannelRechargeMethod::query()
->where('department_id', $player->department_id)
->where('type', 2)
->value('rate');
$playerWithdrawRecord->rate = $rate;
$playerWithdrawRecord->currency = 'USDT';
$playerWithdrawRecord->wallet_address = $playerBank->wallet_address;
$playerWithdrawRecord->qr_code = $playerBank->qr_code;
$playerWithdrawRecord->type = PlayerWithdrawRecord::TYPE_USDT;
$playerWithdrawRecord->status = PlayerWithdrawRecord::STATUS_WAIT;
$playerWithdrawRecord->save();
//三方提现
} else {
$params = [
'amount' => $money,
'bankCode' => $playerBank->bank_code,
'bankAccountNo' => $playerBank->account,
'bankAccountName' => $playerBank->account_name,
'orderNo' => $orderNo,
];
$res = (new SklPayServices())->payout($params);
if ($res['code'] == 'success') {
$playerWithdrawRecord->bank_name = $playerBank->bank_name;
$playerWithdrawRecord->bank_code = $playerBank->bank_code;
$playerWithdrawRecord->account = $playerBank->account;
$playerWithdrawRecord->account_name = $playerBank->account_name;
$playerWithdrawRecord->talk_tradeno = $res['data']['reference_number'];
$playerWithdrawRecord->type = PlayerWithdrawRecord::TYPE_SKLPAYOUT;
$playerWithdrawRecord->status = PlayerWithdrawRecord::STATUS_WAIT;
$playerWithdrawRecord->save();
} else {
return jsonFailResponse($res['error']);
}
}
DB::commit();
} catch (\Exception $e) {
DB::rollBack();
return jsonFailResponse(trans('system_error', [], 'message') . $e->getMessage());
}
// 更新玩家统计
$player->player_extend->withdraw_amount = bcadd($player->player_extend->withdraw_amount, $playerWithdrawRecord->coins, 2);
$player->push();
//寫入金流明細
$playerDeliveryRecord = new PlayerDeliveryRecord;
$playerDeliveryRecord->player_id = $playerWithdrawRecord->player_id;
$playerDeliveryRecord->department_id = $playerWithdrawRecord->department_id;
$playerDeliveryRecord->target = $playerWithdrawRecord->getTable();
$playerDeliveryRecord->target_id = $playerWithdrawRecord->id;
$playerDeliveryRecord->type = PlayerDeliveryRecord::TYPE_WITHDRAWAL;
$playerDeliveryRecord->source = 'channel_withdrawal';
$playerDeliveryRecord->amount = $playerWithdrawRecord->coins;
$playerDeliveryRecord->amount_before = $beforeGameAmount;
$playerDeliveryRecord->amount_after = $player->wallet->money;
$playerDeliveryRecord->tradeno = $playerWithdrawRecord->tradeno ?? '';
$playerDeliveryRecord->remark = '';
$playerDeliveryRecord->save();
return jsonSuccessResponse('success', [
'tradeno' => $playerWithdrawRecord->tradeno,
'money' => $playerWithdrawRecord->money,
'currency' => $playerWithdrawRecord->currency,
'status' => $playerWithdrawRecord->status,
]);
}
/**
* 玩家账变记录
* @param Request $request
* @return Response
* @throws PlayerCheckException|PromoterCheckException
*/
public function playerDeliveryRecord(Request $request): Response
{
$player = checkPlayer(false);
$validator = v::key('page', v::intVal()->setName(trans('page', [], 'message')))
->key('type', v::stringVal()->setName(trans('date_type', [], 'message')))
->key('size', v::intVal()->setName(trans('size', [], 'message')));
$data = $request->all();
try {
$validator->assert($data);
} catch (AllOfException $e) {
return jsonFailResponse(getValidationMessages($e));
}
try {
$playerDeliveryRecordModel = PlayerDeliveryRecord::where('player_id', $player->id);
switch ($data['type']) {
case 'today': // 今天
$playerDeliveryRecordModel->whereDate('created_at', date('Y-m-d'));
break;
case 'week': // 本周
$playerDeliveryRecordModel->where('created_at', '>=', \Carbon\Carbon::today()->startOfWeek())->where('created_at', '<=', Carbon::today()->endOfWeek());
break;
case 'month': // 本月
$playerDeliveryRecordModel->where('created_at', '>=', Carbon::today()->firstOfMonth())->where('created_at', '<=', Carbon::today()->endOfMonth());
break;
case 'sub_month': // 上月
$playerDeliveryRecordModel->where('created_at', '>=', Carbon::today()->subMonth()->firstOfMonth())->where('created_at', '<=', Carbon::today()->subMonth()->endOfMonth());
break;
default:
$playerDeliveryRecordModel->whereDate('created_at', date('Y-m-d'));
break;
}
$inType = implode(',', [
PlayerDeliveryRecord::TYPE_MODIFIED_AMOUNT_ADD,
PlayerDeliveryRecord::TYPE_PRESENT_IN,
PlayerDeliveryRecord::TYPE_GAME_IN,
PlayerDeliveryRecord::TYPE_ACTIVITY_BONUS,
PlayerDeliveryRecord::TYPE_REGISTER_PRESENT,
PlayerDeliveryRecord::TYPE_PROFIT,
PlayerDeliveryRecord::TYPE_LOTTERY,
]);
$outType = implode(',', [
PlayerDeliveryRecord::TYPE_PRESENT_OUT,
PlayerDeliveryRecord::TYPE_GAME_OUT,
PlayerDeliveryRecord::TYPE_MODIFIED_AMOUNT_DEDUCT,
]);
$totalModel = clone $playerDeliveryRecordModel;
$totalData = $totalModel->selectRaw('sum(IF(type in (' . $inType . '), amount, 0)) as total_in, sum(IF(type in (' . $outType . '), amount, 0)) as total_out, sum(IF(type = ' . PlayerDeliveryRecord::TYPE_WITHDRAWAL . ', amount, 0)) as total_withdrawal, sum(IF(type = ' . PlayerDeliveryRecord::TYPE_WITHDRAWAL_BACK . ', amount, 0)) as total_withdrawal_back, sum(IF(type = ' . PlayerDeliveryRecord::TYPE_RECHARGE . ', amount, 0)) as total_recharge')->first();
$playerDeliveryRecord = $playerDeliveryRecordModel->forPage($data['page'], $data['size'])
->orderBy('id', 'desc')
->get();
$list = [];
/** @var PlayerDeliveryRecord $item */
foreach ($playerDeliveryRecord as $item) {
switch ($item->type) {
case PlayerDeliveryRecord::TYPE_MODIFIED_AMOUNT_ADD:
$item->target = trans('target.modified_amount_add', [], 'message');
break;
case PlayerDeliveryRecord::TYPE_PRESENT_IN:
$item->target = trans('target.present_in', [], 'message');
break;
case PlayerDeliveryRecord::TYPE_PRESENT_OUT:
$item->target = trans('target.present_out', [], 'message');
$item->amount = '-' . $item->amount;
break;
case PlayerDeliveryRecord::TYPE_GAME_OUT:
$item->target = trans('target.machine_up', [], 'message');
$item->amount = '-' . $item->amount;
break;
case PlayerDeliveryRecord::TYPE_GAME_IN:
$item->target = trans('target.machine_down', [], 'message');
break;
case PlayerDeliveryRecord::TYPE_RECHARGE:
switch ($item->source) {
case 'artificial_recharge':
$item->target = trans('target.artificial_recharge', [], 'message');
break;
case 'self_recharge':
$item->target = trans('target.self_recharge', [], 'message');
break;
case 'talk_recharge':
$item->target = trans('target.talk_recharge', [], 'message');
break;
case 'coin_recharge':
$item->target = trans('target.coin_recharge', [], 'message');
break;
}
break;
case PlayerDeliveryRecord::TYPE_WITHDRAWAL:
switch ($item['source']) {
case 'artificial_withdrawal':
$item->target = trans('target.artificial_withdrawal', [], 'message');
break;
case 'talk_withdrawal':
$item->target = trans('target.talk_withdrawal', [], 'message');
break;
case 'channel_withdrawal':
$item->target = trans('target.channel_withdrawal', [], 'message');
break;
}
$item->amount = '-' . $item->amount;
break;
case PlayerDeliveryRecord::TYPE_MODIFIED_AMOUNT_DEDUCT:
$item->target = trans('target.modified_amount_deduct', [], 'message');
$item->amount = '-' . $item->amount;
break;
case PlayerDeliveryRecord::TYPE_WITHDRAWAL_BACK:
$item->target = trans('target.withdrawal_back', [], 'message');
break;
case PlayerDeliveryRecord::TYPE_ACTIVITY_BONUS:
$item->target = trans('target.activity_bonus', [], 'message');
break;
case PlayerDeliveryRecord::TYPE_REGISTER_PRESENT:
$item->target = trans('target.register_present', [], 'message');
break;
case PlayerDeliveryRecord::TYPE_PROFIT:
$item->target = trans('target.profit', [], 'message');
break;
default:
break;
}
$list[] = [
'id' => $item->id,
'amount' => $item->amount,
'source' => $item->target,
'amount_after' => $item->amount_after,
'created_at' => date('Y-m-d H:i:s', strtotime($item->created_at)),
];
}
} catch (\Exception $e) {
return jsonFailResponse($e->getMessage());
}
return jsonSuccessResponse('success', [
'list' => $list,
'player' => [
'uuid' => $player->uuid,
'name' => $player->name,
'promoter_uuid' => $player->uuid,
],
'total_data' => [
'total_in' => $totalData['total_in'] ?? 0,
'total_out' => $totalData['total_out'] ?? 0,
'total_withdrawal' => bcsub($totalData['total_withdrawal'] ?? 0, $totalData['total_withdrawal_back'] ?? 0, 2),
'total_recharge' => $totalData['total_recharge'] ?? 0,
],
'date_type' => [
'today' => Carbon::today()->format('Y-m-d'),
'week' => Carbon::today()->startOfWeek()->format('Y-m-d') . '~' . Carbon::today()->endOfWeek()->format('Y-m-d'),
'month' => Carbon::today()->firstOfMonth()->format('Y-m-d') . '~' . Carbon::today()->endOfMonth()->format('Y-m-d'),
'sub_month' => Carbon::today()->subMonth()->firstOfMonth()->format('Y-m-d') . '~' . Carbon::today()->subMonth()->endOfMonth()->format('Y-m-d'),
]
]);
}
/**
* 游戏记录
* @param Request $request
* @return Response
* @throws PlayerCheckException|GameException
*/
public function gameRecord(Request $request): Response
{
$player = checkPlayer();
$data = $request->all();
$validator = v::key('page', v::intVal()->setName(trans('page', [], 'message')))
->key('size', v::intVal()->setName(trans('size', [], 'message')));
try {
$validator->assert($data);
} catch (AllOfException $e) {
return jsonFailResponse(getValidationMessages($e));
}
$records = DrawRecord::query()
->select('prize_pic', 'prize_name', 'prize_type', 'draw_time', 'ip')
->where('uid', $player->id)
->orderBy('id', 'desc')
->forPage($data['page'], $data['size'])
->get()
->toArray();
return jsonSuccessResponse('success', $records);
}
/**
* 充提记录
* @param Request $request
* @return Response
* @throws PlayerCheckException|GameException
*/
public function depositWithdrawalRecord(Request $request): Response
{
$player = checkPlayer();
$data = $request->all();
$validator = v::key('page', v::intVal()->setName(trans('page', [], 'message')))
->key('size', v::intVal()->setName(trans('size', [], 'message')))
->key('type', v::intVal()->setName(trans('type', [], 'message')))
->key('start_date', v::stringVal()->setName(trans('start_date', [], 'message')))
->key('end_date', v::stringVal()->setName(trans('end_date', [], 'message')));
try {
$validator->assert($data);
} catch (AllOfException $e) {
return jsonFailResponse(getValidationMessages($e));
}
// 1. 构建充值查询
$rechargeQuery = DB::table('player_recharge_record')
->select('money', 'created_at', 'status', DB::raw("1 as type"))
->where('player_id', $player->id)
->whereBetween('created_at', [$data['start_date'],date('Y-m-d', strtotime("{$data['end_date']} +1 days"))]);
// 2. 构建提现查询
$withdrawQuery = DB::table('player_withdraw_record')
->select('money', 'created_at', 'status', DB::raw("2 as type"))
->where('player_id', $player->id)
->whereBetween('created_at', [$data['start_date'],date('Y-m-d', strtotime("{$data['end_date']} +1 days"))]);
// 3. 根据类型参数过滤
if ($data['type'] == 1) {
$query = $rechargeQuery;
} elseif ($data['type'] == 2) {
$query = $withdrawQuery;
} else {
$query = $rechargeQuery->unionAll($withdrawQuery);
}
// 4. 处理排序和分页
$records = DB::table(DB::raw("({$query->toSql()}) as combined_transactions"))
->mergeBindings($query) // 重要:合并 SQL 绑定参数
->orderBy('created_at', 'desc')
->forPage($data['page'], $data['size'])
->get()->toArray();
return jsonSuccessResponse('success', $records);
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace app\api\controller\v1;
use addons\webman\model\AppVersion;
use Exception;
use support\Request;
use support\Response;
class VersionController
{
/**
* 登录接口
* @param Request $request
* @return Response
* @throws Exception
*/
public function getAppUpdate(Request $request): Response
{
// 更新比较时,查询的是版本标识,大于传入的版本标识时,返回新版本的数据
$version_key = $request->header('app-version-key', '');
// 传入检测的系统
$system_key = $request->header('system-key', '');
if (empty($version_key) || empty($system_key)) {
return jsonFailResponse(trans('incomplete_parameters', [], 'message'));
}
$data = AppVersion::query()
->where('app_version_key', '>=', $version_key)
->where(['status' => 1])
->where('status', 1)
->where('system_key', $system_key)
->where('department_id', request()->department_id)
->whereDate('regular_update', '<', date("Y-m-d H:i:s", time()))
->select(['id', 'system_key', 'app_version', 'app_version_key', 'apk_url', 'hot_update_url', 'force_update', 'hot_update', 'regular_update', 'update_content', 'notes'])
->orderBy('id', 'desc')
->first();
return jsonSuccessResponse('success', [
'data' => $data
]);
}
}

View File

@@ -0,0 +1,50 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace app\exception;
use Respect\Validation\Exceptions\NestedValidationException;
use Respect\Validation\Exceptions\ValidationException;
use Throwable;
use Tinywan\Jwt\Exception\JwtTokenException;
use Tinywan\Jwt\Exception\JwtTokenExpiredException;
use Webman\Exception\ExceptionHandler;
use Webman\Http\Request;
use Webman\Http\Response;
/**
* Class Handler
* @package support\exception
*/
class ApiHandler extends ExceptionHandler
{
public $dontReport = [
NestedValidationException::class,
ValidationException::class,
];
public function render(Request $request, Throwable $exception): Response
{
if ($exception instanceof JwtTokenExpiredException || $exception instanceof JwtTokenException) {
return json([
'code' => 401,
'msg' => $exception->getMessage()
]);
}
return json([
'code' => $exception->getCode(),
'msg' => $exception->getMessage()
]);
}
}

View File

@@ -0,0 +1,6 @@
<?php
namespace app\exception;
class GameException extends \Exception
{
}

View File

@@ -0,0 +1,6 @@
<?php
namespace app\exception;
class PlayerCheckException extends \Exception
{
}

View File

@@ -0,0 +1,7 @@
<?php
namespace app\exception;
class PromoterCheckException extends \Exception
{
}

260
app/external/ExternalApiController.php vendored Normal file
View File

@@ -0,0 +1,260 @@
<?php
namespace app\external;
use addons\webman\model\Broadcast;
use addons\webman\model\Notice;
use addons\webman\model\PlayerGamePlatform;
use addons\webman\model\PlayerRechargeRecord;
use addons\webman\model\Player;
use addons\webman\model\PlayerDeliveryRecord;
use addons\webman\model\PlayerWithdrawRecord;
use app\service\game\MeGa888ServiceInterface;
use app\service\OnePayServices;
use app\service\SePayServices;
use app\service\SklPayServices;
use Respect\Validation\Exceptions\AllOfException;
use Respect\Validation\Validator as v;
use support\Db;
use support\Request;
use support\Log;
use support\Response;
use Webman\Push\PushException;
use Webman\RedisQueue\Redis;
class ExternalApiController
{
/** 排除 */
protected $noNeedSign = [];
/**
* 网页注册
* @return Response
*/
public function login(): Response
{
return view('login/index');
}
/**
* app下载
* @return Response
*/
public function download(): Response
{
return view('login/download');
}
/**
* SKL支付回调
* @param Request $request
* @return string
* @throws PushException
*/
public function sklRechargeNotify(Request $request): string
{
$data = $request->all();
if ($data['status'] && $data['invoice_no']) {
/** @var PlayerRechargeRecord $playerRechargeRecord */
$playerRechargeRecord = PlayerRechargeRecord::query()->where('tradeno', $data['invoice_no'])->where('status', 0)->first();
if(empty($playerRechargeRecord) || $playerRechargeRecord->money != $data['amount']){
return 'FAIL';
}
if ($data['status'] == 'SUCCESS') {
if ($playerRechargeRecord->gift_coins > 0) {
$firstRecharge = PlayerRechargeRecord::query()
->where('player_id', $playerRechargeRecord->player_id)
->where('status', 2)
->where('setting_id', $playerRechargeRecord->setting_id)
->first();
if ($firstRecharge) {
$playerRechargeRecord->gift_coins = 0;
}
}
$addMoney = bcadd($playerRechargeRecord->coins, $playerRechargeRecord->gift_coins, 2);
DB::beginTransaction();
try {
/** @var Player $player */
$player = Player::query()->find($playerRechargeRecord->player_id);
$playerRechargeRecord->status = PlayerRechargeRecord::STATUS_RECHARGED_SUCCESS;
$playerRechargeRecord->finish_time = date("Y-m-d H:i:s");
$playerRechargeRecord->notify_result = json_encode($data);
$playerRechargeRecord->save();
$beforeGameAmount = $player->wallet->money;
// 更新钱包
$player->wallet->money = bcadd($player->wallet->money, $addMoney, 2);
$player->player_extend->recharge_amount = bcadd($player->player_extend->recharge_amount, $addMoney, 2);
$player->push();
//寫入金流明細
$playerDeliveryRecord = new PlayerDeliveryRecord;
$playerDeliveryRecord->player_id = $player->id;
$playerDeliveryRecord->department_id = $player->department_id;
$playerDeliveryRecord->target = $playerRechargeRecord->getTable();
$playerDeliveryRecord->target_id = $playerRechargeRecord->id;
$playerDeliveryRecord->type = PlayerDeliveryRecord::TYPE_RECHARGE;
$playerDeliveryRecord->source = 'self_recharge';
$playerDeliveryRecord->amount = $addMoney;
$playerDeliveryRecord->amount_before = $beforeGameAmount;
$playerDeliveryRecord->amount_after = $player->wallet->money;
$playerDeliveryRecord->tradeno = '';
$playerDeliveryRecord->remark = '';
$playerDeliveryRecord->save();
DB::commit();
sendSocketMessage('private-recharge_withdrawal', [
'msg_type' => 'recharge',
'player_id' => $player->id,
'amount' => $player->wallet->money,
]);
} catch (\Exception $e) {
DB::rollBack();
Log::info('sklRecharge', (array)$data);
return json_encode(['status' => 'RECEIVED']);
}
return json_encode(['status' => 'RECEIVED']);
} else {
$playerRechargeRecord->status = PlayerRechargeRecord::STATUS_RECHARGED_FAIL;
$playerRechargeRecord->cancel_time = date('Y-m-d H:i:s');
$playerRechargeRecord->notify_result = json_encode($data);
$playerRechargeRecord->save();
return json_encode(['status' => 'RECEIVED']);
}
} else {
return json_encode(['status' => 'RECEIVED']);
}
}
/**
* SKL代付回调
* @param Request $request
* @return string
*/
public function sklWithdrawalNotify(Request $request): string
{
$data = $request->all();
if ($data['status'] && $data['invoice_no']) {
//查询订单是否存在
/** @var PlayerWithdrawRecord $playerWithdrawRecord */
$playerWithdrawRecord = PlayerWithdrawRecord::query()->where('tradeno', $data['invoice_no'])->where('status', 1)->first();
if(empty($playerWithdrawRecord) || $playerWithdrawRecord->money != $data['amount']){
return 'FAIL';
}
if ($data['status'] == 'SUCCESS') {
$playerWithdrawRecord->status = PlayerWithdrawRecord::STATUS_SUCCESS;
$playerWithdrawRecord->finish_time = date('Y-m-d H:i:s');
$playerWithdrawRecord->notify_result = json_encode($data);
$playerWithdrawRecord->talk_tradeno = $data['transferOut_id'];
$playerWithdrawRecord->save();
$broadcast = Broadcast::query()
->where('type', 2)
->where('status', 1)
->where('min_money', '<=', $data['amount'])
->first();
if (isset($broadcast)){
$queue = 'broadcast_tasks';
$broadcast_data = [
'user_id' => substr($playerWithdrawRecord->player->phone, 2),
'money' => $data['amount']
];
for ($i = 0; $i < $broadcast->copy_num; $i++) {
Redis::send($queue, $broadcast_data);
}
}
$notice = new Notice();
$notice->department_id = $playerWithdrawRecord->player->department_id;
$notice->player_id = $playerWithdrawRecord->player_id;
$notice->source_id = $playerWithdrawRecord->id;
$notice->type = Notice::TYPE_WITHDRAW;
$notice->receiver = Notice::RECEIVER_PLAYER;
$notice->is_private = 1;
$notice->title = '下分成功';
$notice->content = '本次申请已成功处理,下分 ' . $playerWithdrawRecord->money . ' ,请查收。 ';
$notice->save();
return json_encode(['status' => 'RECEIVED']);
} else {
DB::beginTransaction();
try {
/** @var Player $player */
$player = Player::query()->find($playerWithdrawRecord->player_id);
$playerWithdrawRecord->status = PlayerWithdrawRecord::STATUS_FAIL;
$playerWithdrawRecord->cancel_time = date('Y-m-d H:i:s');
$playerWithdrawRecord->notify_result = json_encode($data);
$playerWithdrawRecord->talk_tradeno = $data['transferOut_id'];
$playerWithdrawRecord->save();
$beforeGameAmount = $player->wallet->money;
// 更新钱包
$player->wallet->money = bcadd($player->wallet->money, $playerWithdrawRecord->coins, 2);
$player->push();
//寫入金流明細
$playerDeliveryRecord = new PlayerDeliveryRecord;
$playerDeliveryRecord->player_id = $player->id;
$playerDeliveryRecord->department_id = $player->department_id;
$playerDeliveryRecord->target = $playerWithdrawRecord->getTable();
$playerDeliveryRecord->target_id = $playerWithdrawRecord->id;
$playerDeliveryRecord->type = PlayerDeliveryRecord::TYPE_WITHDRAWAL_BACK;
$playerDeliveryRecord->source = 'channel_withdrawal';
$playerDeliveryRecord->amount = $playerWithdrawRecord->coins;
$playerDeliveryRecord->amount_before = $beforeGameAmount;
$playerDeliveryRecord->amount_after = $player->wallet->money;
$playerDeliveryRecord->tradeno = '';
$playerDeliveryRecord->remark = '提现失败返还金额';
$playerDeliveryRecord->save();
$notice = new Notice();
$notice->department_id = $player->department_id;
$notice->player_id = $player->id;
$notice->source_id = $playerWithdrawRecord->id;
$notice->type = Notice::TYPE_WITHDRAW;
$notice->receiver = Notice::RECEIVER_PLAYER;
$notice->is_private = 1;
$notice->title = '下分失败';
$notice->content = '本次申请下分 ' . $playerWithdrawRecord->money . ' 已退回,请查收。 ';
$notice->save();
DB::commit();
sendSocketMessage('private-recharge_withdrawal', [
'msg_type' => 'withdrawal',
'player_id' => $player->id,
'amount' => $player->wallet->money,
]);
} catch (\Exception $e) {
DB::rollBack();
Log::info('sklWithdrawal', (array)$data);
return json_encode(['status' => 'RECEIVED']);
}
return json_encode(['status' => 'RECEIVED']);
}
} else {
return json_encode(['status' => 'RECEIVED']);
}
}
/**
* SKL订单查询
* @param Request $request
* @return Response
*/
public function sklQuery(Request $request): Response
{
$validator = v::key('transactionId', v::stringType()->notEmpty()->setName(trans('certificate', [], 'message')));
$data = $request->get();
try {
$validator->assert($data);
} catch (AllOfException $e) {
return jsonFailResponse(getValidationMessages($e));
}
$params = [
'orderNo' => $data['transactionId'],
];
$res = (new SklPayServices())->query($params);
if ($res['status'] == 'SUCCESS') {
return view('skl/detail_success');
} elseif ($res['status'] == 'FAILED') {
return view('skl/detail_fail');
} elseif ($res['status'] == 'PENDING_QR') {
return view('skl/detail_paying');
} elseif ($res['status'] == 'VERIFIED') {
return view('skl/detail_paying');
} else {
return view('sepay/detail_fail');
}
}
}

14
app/external/view/login/download.html vendored Normal file
View File

@@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<!-- <link rel="icon" type="image/svg+xml" href="/vite.svg" />-->
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>SamSung88</title>
<script type="module" crossorigin src="/external/assets/index-DrUgyNgB.js"></script>
<link rel="stylesheet" crossorigin href="/external/assets/index-DB4XlpIQ.css">
</head>
<body>
<div id="root"></div>
</body>
</html>

14
app/external/view/login/index.html vendored Normal file
View File

@@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<!-- <link rel="icon" type="image/svg+xml" href="/vite.svg" />-->
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>SamSung88</title>
<script type="module" crossorigin src="/external/assets/index-BZ3xB-zH.js"></script>
<link rel="stylesheet" crossorigin href="/external/assets/index-DB4XlpIQ.css">
</head>
<body>
<div id="root"></div>
</body>
</html>

126
app/external/view/sepay/detail.html vendored Normal file
View File

@@ -0,0 +1,126 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>页面</title>
<style>
/* 页面整体居中 */
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
flex-direction: column;
text-align: center;
}
/* 按钮样式 */
button {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 10px 20px;
margin: 10px;
font-size: 16px;
cursor: pointer;
border: none;
background-color: #4CAF50;
color: white;
border-radius: 5px;
}
/* 按钮图标样式 */
img {
margin-right: 8px;
width: 20px;
height: 20px;
}
/* 隐藏按钮 */
.hidden {
display: none;
}
</style>
<script>
// 获取 URL 中的参数
function getUrlParameter(name) {
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get(name);
}
// 页面加载后根据 URL 参数决定显示的按钮
window.onload = function () {
const status = getUrlParameter('status');
const orderNo = getUrlParameter('orderNo');
// 填充订单号
if (orderNo) {
document.getElementById('orderNoField').innerHTML = `订单号: ${orderNo}`;
document.getElementById('orderNoField1').innerHTML = `订单号: ${orderNo}`;
document.getElementById('orderNoField2').innerHTML = `订单号: ${orderNo}`;
} else {
document.getElementById('orderNoField').innerHTML = '订单号: 未找到';
}
// 根据 type 参数设置按钮
const closeButton = document.getElementById('closeButton');
const refreshButton = document.getElementById('refreshButton');
const loadButton = document.getElementById('loadButton');
closeButton.classList.add('hidden');
refreshButton.classList.add('hidden');
loadButton.classList.add('hidden');
if (status == '1') {
closeButton.classList.remove('hidden');
} else if (status == '2') {
refreshButton.classList.remove('hidden');
} else if (status == '3') {
loadButton.classList.remove('hidden');
}
};
// 关闭页面
function closePage() {
window.close();
}
// 刷新页面
function refreshPage() {
window.location.reload();
}
// 加载页面内容
function loadContent() {
alert('加载内容');
}
</script>
</head>
<body>
<!-- <h1>按钮操作</h1> -->
<div id="closeButton" class="hidden">
<img src="./img/icon01.png" alt="关闭" style="width: 200px;height: 200px;">
<div style="font-size: 18px;margin-top: 30px;">支付成功</div>
<div style="font-size: 18px;margin-top: 30px;"><strong id="orderNoField"></strong></div>
<button style="background-color: #1ab56a;margin-top: 30px;" onclick="closePage()">返回游戏</button>
</div>
<div id="refreshButton" class="hidden">
<img src="./img/icon02.png" alt="关闭" style="width: 200px;height: 200px;">
<div style="font-size: 18px;margin-top: 30px;">支付失败</div>
<div style="font-size: 18px;margin-top: 30px;"><strong id="orderNoField1"></strong></div>
<button style="background-color: #ca2f11;margin-top: 30px;" onclick="closePage()">返回游戏</button>
</div>
<div id="loadButton" class="hidden">
<img src="./img/icon03.png" alt="刷新" style="width: 200px;height: 200px;">
<div style="font-size: 18px;margin-top: 30px;">获取支付状态中...</div>
<div style="font-size: 18px;margin-top: 30px;"><strong id="orderNoField2"></strong></div>
<button style="background-color: #d78e0e;margin-top: 30px;" onclick="refreshPage()">刷新状态</button>
</div>
</body>
</html>

View File

@@ -0,0 +1,91 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>页面</title>
<style>
/* 页面整体居中 */
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
flex-direction: column;
text-align: center;
}
/* 按钮样式 */
button {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 10px 20px;
margin: 10px;
font-size: 16px;
cursor: pointer;
border: none;
background-color: #4CAF50;
color: white;
border-radius: 5px;
}
/* 按钮图标样式 */
img {
margin-right: 8px;
width: 20px;
height: 20px;
}
</style>
<script>
// 获取 URL 中的参数
function getUrlParameter(name) {
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get(name);
}
// 页面加载后根据 URL 参数决定显示的按钮
window.onload = function () {
const orderNo = getUrlParameter('orderNo');
// 填充订单号
if (orderNo) {
document.getElementById('orderNoField').innerHTML = `订单号: ${orderNo}`;
} else {
document.getElementById('orderNoField').innerHTML = '订单号: 未找到';
}
};
// 关闭页面
function closePage() {
window.close();
}
// 刷新页面
function refreshPage() {
window.location.reload();
}
// 加载页面内容
function loadContent() {
alert('加载内容');
}
</script>
</head>
<body>
<!-- <h1>按钮操作</h1> -->
<div id="refreshButton" class="hidden">
<img src="./img/icon02.png" alt="关闭" style="width: 200px;height: 200px;">
<div style="font-size: 18px;margin-top: 30px;">支付失败</div>
<div style="font-size: 18px;margin-top: 30px;"><strong id="orderNoField"></strong></div>
<button style="background-color: #ca2f11;margin-top: 30px;" onclick="closePage()">返回游戏</button>
</div>
</body>
</html>

View File

@@ -0,0 +1,90 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>页面</title>
<style>
/* 页面整体居中 */
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
flex-direction: column;
text-align: center;
}
/* 按钮样式 */
button {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 10px 20px;
margin: 10px;
font-size: 16px;
cursor: pointer;
border: none;
background-color: #4CAF50;
color: white;
border-radius: 5px;
}
/* 按钮图标样式 */
img {
margin-right: 8px;
width: 20px;
height: 20px;
}
</style>
<script>
// 获取 URL 中的参数
function getUrlParameter(name) {
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get(name);
}
// 页面加载后根据 URL 参数决定显示的按钮
window.onload = function () {
const orderNo = getUrlParameter('orderNo');
// 填充订单号
if (orderNo) {
document.getElementById('orderNoField').innerHTML = `订单号: ${orderNo}`;
} else {
document.getElementById('orderNoField').innerHTML = '订单号: 未找到';
}
};
// 关闭页面
function closePage() {
window.close();
}
// 刷新页面
function refreshPage() {
window.location.reload();
}
// 加载页面内容
function loadContent() {
alert('加载内容');
}
</script>
</head>
<body>
<!-- <h1>按钮操作</h1> -->
<div id="loadButton" class="hidden">
<img src="./img/icon03.png" alt="刷新" style="width: 200px;height: 200px;">
<div style="font-size: 18px;margin-top: 30px;">获取支付状态中...</div>
<div style="font-size: 18px;margin-top: 30px;"><strong id="orderNoField"></strong></div>
<button style="background-color: #d78e0e;margin-top: 30px;" onclick="refreshPage()">刷新状态</button>
</div>
</body>
</html>

View File

@@ -0,0 +1,91 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>页面</title>
<style>
/* 页面整体居中 */
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
flex-direction: column;
text-align: center;
}
/* 按钮样式 */
button {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 10px 20px;
margin: 10px;
font-size: 16px;
cursor: pointer;
border: none;
background-color: #4CAF50;
color: white;
border-radius: 5px;
}
/* 按钮图标样式 */
img {
margin-right: 8px;
width: 20px;
height: 20px;
}
</style>
<script>
// 获取 URL 中的参数
function getUrlParameter(name) {
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get(name);
}
// 页面加载后根据 URL 参数决定显示的按钮
window.onload = function () {
const orderNo = getUrlParameter('orderNo');
// 填充订单号
if (orderNo) {
document.getElementById('orderNoField').innerHTML = `订单号: ${orderNo}`;
} else {
document.getElementById('orderNoField').innerHTML = '订单号: 未找到';
}
};
// 关闭页面
function closePage() {
window.close();
}
// 刷新页面
function refreshPage() {
window.location.reload();
}
// 加载页面内容
function loadContent() {
alert('加载内容');
}
</script>
</head>
<body>
<!-- <h1>按钮操作</h1> -->
<div id="closeButton">
<img src="./img/icon01.png" alt="关闭" style="width: 200px;height: 200px;">
<div style="font-size: 18px;margin-top: 30px;">支付成功</div>
<div style="font-size: 18px;margin-top: 30px;"><strong id="orderNoField"></strong></div>
<button style="background-color: #1ab56a;margin-top: 30px;" onclick="closePage()">返回游戏</button>
</div>
</body>
</html>

126
app/external/view/skl/detail.html vendored Normal file
View File

@@ -0,0 +1,126 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>页面</title>
<style>
/* 页面整体居中 */
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
flex-direction: column;
text-align: center;
}
/* 按钮样式 */
button {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 10px 20px;
margin: 10px;
font-size: 16px;
cursor: pointer;
border: none;
background-color: #4CAF50;
color: white;
border-radius: 5px;
}
/* 按钮图标样式 */
img {
margin-right: 8px;
width: 20px;
height: 20px;
}
/* 隐藏按钮 */
.hidden {
display: none;
}
</style>
<script>
// 获取 URL 中的参数
function getUrlParameter(name) {
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get(name);
}
// 页面加载后根据 URL 参数决定显示的按钮
window.onload = function () {
const status = getUrlParameter('status');
const orderNo = getUrlParameter('orderNo');
// 填充订单号
if (orderNo) {
document.getElementById('transactionId').innerHTML = `订单号: ${orderNo}`;
document.getElementById('orderNoField1').innerHTML = `订单号: ${orderNo}`;
document.getElementById('orderNoField2').innerHTML = `订单号: ${orderNo}`;
} else {
document.getElementById('orderNoField').innerHTML = '订单号: 未找到';
}
// 根据 type 参数设置按钮
const closeButton = document.getElementById('closeButton');
const refreshButton = document.getElementById('refreshButton');
const loadButton = document.getElementById('loadButton');
closeButton.classList.add('hidden');
refreshButton.classList.add('hidden');
loadButton.classList.add('hidden');
if (status == '1') {
closeButton.classList.remove('hidden');
} else if (status == '2') {
refreshButton.classList.remove('hidden');
} else if (status == '3') {
loadButton.classList.remove('hidden');
}
};
// 关闭页面
function closePage() {
window.close();
}
// 刷新页面
function refreshPage() {
window.location.reload();
}
// 加载页面内容
function loadContent() {
alert('加载内容');
}
</script>
</head>
<body>
<!-- <h1>按钮操作</h1> -->
<div id="closeButton" class="hidden">
<img src="./img/icon01.png" alt="关闭" style="width: 200px;height: 200px;">
<div style="font-size: 18px;margin-top: 30px;">支付成功</div>
<div style="font-size: 18px;margin-top: 30px;"><strong id="orderNoField"></strong></div>
<button style="background-color: #1ab56a;margin-top: 30px;" onclick="closePage()">返回游戏</button>
</div>
<div id="refreshButton" class="hidden">
<img src="./img/icon02.png" alt="关闭" style="width: 200px;height: 200px;">
<div style="font-size: 18px;margin-top: 30px;">支付失败</div>
<div style="font-size: 18px;margin-top: 30px;"><strong id="orderNoField1"></strong></div>
<button style="background-color: #ca2f11;margin-top: 30px;" onclick="closePage()">返回游戏</button>
</div>
<div id="loadButton" class="hidden">
<img src="./img/icon03.png" alt="刷新" style="width: 200px;height: 200px;">
<div style="font-size: 18px;margin-top: 30px;">获取支付状态中...</div>
<div style="font-size: 18px;margin-top: 30px;"><strong id="orderNoField2"></strong></div>
<button style="background-color: #d78e0e;margin-top: 30px;" onclick="refreshPage()">刷新状态</button>
</div>
</body>
</html>

97
app/external/view/skl/detail_fail.html vendored Normal file
View File

@@ -0,0 +1,97 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>页面</title>
<style>
/* 页面整体居中 */
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
flex-direction: column;
text-align: center;
}
/* 按钮样式 */
button {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 10px 20px;
margin: 10px;
font-size: 16px;
cursor: pointer;
border: none;
background-color: #4CAF50;
color: white;
border-radius: 5px;
}
/* 按钮图标样式 */
img {
margin-right: 8px;
width: 20px;
height: 20px;
}
</style>
<script>
// 获取 URL 中的参数
function getInvoiceNoFromUrl(name) {
try {
const fullUrl = window.location.href;
const params = new URL(fullUrl).searchParams;
const rawValue = params.get(name);
if (!rawValue) return "";
return rawValue.split("?")[0];
} catch (err) {
return "";
}
}
// 页面加载后根据 URL 参数决定显示的按钮
window.onload = function () {
const orderNo = getInvoiceNoFromUrl('invoiceNo');
// 填充订单号
if (orderNo) {
document.getElementById('orderNoField').innerHTML = `订单号: ${orderNo}`;
} else {
document.getElementById('orderNoField').innerHTML = '订单号: 未找到';
}
};
// 关闭页面
function closePage() {
window.close();
}
// 刷新页面
function refreshPage() {
window.location.reload();
}
// 加载页面内容
function loadContent() {
alert('加载内容');
}
</script>
</head>
<body>
<!-- <h1>按钮操作</h1> -->
<div id="refreshButton" class="hidden">
<img src="../img/icon02.png" alt="关闭" style="width: 200px;height: 200px;">
<div style="font-size: 18px;margin-top: 30px;">支付失败</div>
<div style="font-size: 18px;margin-top: 30px;"><strong id="orderNoField"></strong></div>
<button style="background-color: #ca2f11;margin-top: 30px;" onclick="closePage()">返回游戏</button>
</div>
</body>
</html>

View File

@@ -0,0 +1,97 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>页面</title>
<style>
/* 页面整体居中 */
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
flex-direction: column;
text-align: center;
}
/* 按钮样式 */
button {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 10px 20px;
margin: 10px;
font-size: 16px;
cursor: pointer;
border: none;
background-color: #4CAF50;
color: white;
border-radius: 5px;
}
/* 按钮图标样式 */
img {
margin-right: 8px;
width: 20px;
height: 20px;
}
</style>
<script>
// 获取 URL 中的参数
function getInvoiceNoFromUrl(name) {
try {
const fullUrl = window.location.href;
const params = new URL(fullUrl).searchParams;
const rawValue = params.get(name);
if (!rawValue) return "";
return rawValue.split("?")[0];
} catch (err) {
return "";
}
}
// 页面加载后根据 URL 参数决定显示的按钮
window.onload = function () {
const orderNo = getInvoiceNoFromUrl('invoiceNo');
// 填充订单号
if (orderNo) {
document.getElementById('orderNoField').innerHTML = `订单号: ${orderNo}`;
} else {
document.getElementById('orderNoField').innerHTML = '订单号: 未找到';
}
};
// 关闭页面
function closePage() {
window.close();
}
// 刷新页面
function refreshPage() {
window.location.reload();
}
// 加载页面内容
function loadContent() {
alert('加载内容');
}
</script>
</head>
<body>
<!-- <h1>按钮操作</h1> -->
<div id="loadButton" class="hidden">
<img src="../img/icon03.png" alt="刷新" style="width: 200px;height: 200px;">
<div style="font-size: 18px;margin-top: 30px;">获取支付状态中...</div>
<div style="font-size: 18px;margin-top: 30px;"><strong id="orderNoField"></strong></div>
<button style="background-color: #d78e0e;margin-top: 30px;" onclick="refreshPage()">刷新状态</button>
</div>
</body>
</html>

View File

@@ -0,0 +1,97 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>页面</title>
<style>
/* 页面整体居中 */
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
flex-direction: column;
text-align: center;
}
/* 按钮样式 */
button {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 10px 20px;
margin: 10px;
font-size: 16px;
cursor: pointer;
border: none;
background-color: #4CAF50;
color: white;
border-radius: 5px;
}
/* 按钮图标样式 */
img {
margin-right: 8px;
width: 20px;
height: 20px;
}
</style>
<script>
// 获取 URL 中的参数
function getInvoiceNoFromUrl(name) {
try {
const fullUrl = window.location.href;
const params = new URL(fullUrl).searchParams;
const rawValue = params.get(name);
if (!rawValue) return "";
return rawValue.split("?")[0];
} catch (err) {
return "";
}
}
// 页面加载后根据 URL 参数决定显示的按钮
window.onload = function () {
const orderNo = getInvoiceNoFromUrl('invoiceNo');
// 填充订单号
if (orderNo) {
document.getElementById('orderNoField').innerHTML = `订单号: ${orderNo}`;
} else {
document.getElementById('orderNoField').innerHTML = '订单号: 未找到';
}
};
// 关闭页面
function closePage() {
window.close();
}
// 刷新页面
function refreshPage() {
window.location.reload();
}
// 加载页面内容
function loadContent() {
alert('加载内容');
}
</script>
</head>
<body>
<!-- <h1>按钮操作</h1> -->
<div id="closeButton">
<img src="../img/icon01.png" alt="关闭" style="width: 200px;height: 200px;">
<div style="font-size: 18px;margin-top: 30px;">支付成功</div>
<div style="font-size: 18px;margin-top: 30px;"><strong id="orderNoField"></strong></div>
<button style="background-color: #1ab56a;margin-top: 30px;" onclick="closePage()">返回游戏</button>
</div>
</body>
</html>

1379
app/functions.php Normal file
View File

@@ -0,0 +1,1379 @@
<?php
/**
* Here is your custom functions.
*/
use addons\webman\Admin;
use addons\webman\model\Channel;
use addons\webman\model\ChannelFinancialRecord;
use addons\webman\model\CommissionRecord;
use addons\webman\model\GamePlatform;
use addons\webman\model\PhoneSmsLog;
use addons\webman\model\Player;
use addons\webman\model\PlayerBankruptcyRecord;
use addons\webman\model\PlayerChipRecord;
use addons\webman\model\PlayerDeliveryRecord;
use addons\webman\model\PlayerExtend;
use addons\webman\model\PlayerLoginRecord;
use addons\webman\model\PlayerMoneyEditLog;
use addons\webman\model\PlayerPlatformCash;
use addons\webman\model\PlayerRechargeRecord;
use addons\webman\model\PlayerRegisterRecord;
use addons\webman\model\PlayerWalletTransfer;
use addons\webman\model\PlayerWithdrawRecord;
use addons\webman\model\PlayGameRecord;
use addons\webman\model\PromoterProfitSettlementRecord;
use addons\webman\model\SystemSetting;
use app\exception\GameException;
use app\exception\PlayerCheckException;
use app\service\game\GameServiceFactory;
use Carbon\Carbon;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Arr;
use Respect\Validation\Exceptions\AllOfException;
use support\Cache;
use support\Db;
use support\Log;
use support\Model;
use support\Response;
use think\Exception;
use Tinywan\Jwt\JwtToken;
use Webman\Push\Api;
use Webman\Push\PushException;
use WebmanTech\LaravelHttpClient\Facades\Http;
use Workbunny\WebmanIpAttribution\Exceptions\IpAttributionException;
use Workbunny\WebmanIpAttribution\Location;
use addons\webman\model\PlayerPromoter;
use addons\webman\model\PromoterProfitRecord;
/**
* @param array $data
* @param string $message
* @return Response
*/
function jsonSuccessResponse(string $message = '', array $data = []): Response
{
return new Response(200, ['Content-Type' => 'application/json'], json_encode([
'code' => 200,
'msg' => $message,
'data' => $data,
], JSON_UNESCAPED_UNICODE));
}
/**
* @param array $data
* @param string $message
* @param integer $code
* @return Response
*/
function jsonFailResponse(string $message = '', array $data = [], int $code = 100): Response
{
return new Response(200, ['Content-Type' => 'application/json'], json_encode([
'code' => $code,
'msg' => $message,
'data' => $data,
], JSON_UNESCAPED_UNICODE));
}
/**
* 生成唯一邀请码
* @return string
*/
function createCode(): string
{
$code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$rand = $code[rand(0, 25)] . strtoupper(dechex(date('m'))) . date('d') . substr(time(), -5) . substr(microtime(), 2, 5) . sprintf('%02d', rand(0, 99));
for ($a = md5($rand, true), $s = '0123456789ABCDEFGHIJKLMNOPQRSTUV', $d = '', $f = 0; $f < 8; $g = ord($a[$f]), $d .= $s[($g ^ ord($a[$f + 8])) - $g & 0x1F], $f++) ;
return $d;
}
/**
* 获取验证消息
* @param AllOfException $e
* @return mixed
*/
function getValidationMessages(AllOfException $e)
{
$message = $e->getMessages([
'notOptional' => trans('required', [], 'validator'),
'notEmpty' => trans('required', [], 'validator'),
'email' => trans('email', [], 'validator'),
'idCard' => trans('idCard', [], 'validator'),
'url' => trans('url', [], 'validator'),
'number' => trans('number', [], 'validator'),
'integer' => trans('integer', [], 'validator'),
'float' => trans('float', [], 'validator'),
'mobile' => trans('mobile', [], 'validator'),
'length' => trans('length', [], 'validator'),
'alpha' => trans('alpha', [], 'validator'),
'alnum' => trans('alnum', [], 'validator'),
'alphaDash' => trans('alphaDash', [], 'validator'),
'chs' => trans('chs', [], 'validator'),
'chsAlpha' => trans('chsAlpha', [], 'validator'),
'chsAlphaNum' => trans('chsAlphaNum', [], 'validator'),
'chsDash' => trans('chsDash', [], 'validator'),
'equals' => trans('equals', [], 'validator'),
'in' => trans('in', [], 'validator'),
'image' => trans('image', [], 'validator'),
'creditCard' => trans('creditCard', [], 'validator'),
'digit' => trans('digit', [], 'validator'),
'base64' => trans('base64', [], 'validator'),
'arrayVal' => trans('arrayVal', [], 'validator'),
])['key'];
$message = is_array($message) ? Arr::first($message) : $message;
return $message ?? trans('validation_error', [], 'message');
}
/**
* 生成uuid
* @return string
*/
function gen_uuid(): string
{
do {
$timestamp = time();
$randomNumber = str_pad(rand(0, 9999), 4, '0', STR_PAD_LEFT);
$uniqueNumericId = substr($timestamp, -5) . $randomNumber;
} while (Player::query()->where('uuid', $uniqueNumericId)->withTrashed()->exists());
return $uniqueNumericId;
}
/**
* 金额转换
* @param $number
* @return float|int
*/
function floorToCoinsSecondNumber($number)
{
return floor($number * 100) / 100;
}
/**
* 添加用户登录信息
* @param $id
* @return PlayerLoginRecord|Model
*/
function addLoginRecord($id)
{
$ip = request()->getRealIp();
if (!empty($ip)) {
try {
$location = new Location();
$result = $location->getLocation($ip);
} catch (IpAttributionException $exception) {
Log::error('获取ip信息错误');
}
}
$country_name = ($result['country'] ?? '') . ($result['city'] ?? '');
$domain = isset($_SERVER['HTTP_ORIGIN']) ? parse_url($_SERVER['HTTP_ORIGIN']) : null;
return PlayerLoginRecord::create([
'player_id' => $id,
'login_domain' => !empty($domain) ? $domain['host'] : null,
'ip' => $ip,
'country_name' => $country_name,
'city_name' => $result['city'] ?? '',
'remark' => $request->remark ?? null,
'department_id' => request()->department_id,
]);
}
/**
* 添加用户注册信息
* @param $id
* @param $type
* @param $department_id
* @return PlayerRegisterRecord|Model
*/
function addRegisterRecord($id, $type, $department_id)
{
$ip = request()->getRealIp();
if (!empty($ip)) {
try {
$location = new Location();
$result = $location->getLocation($ip);
} catch (IpAttributionException $exception) {
Log::error('获取ip信息错误');
}
}
$country_name = ($result['country'] ?? '') . ($result['city'] ?? '');
$domain = isset($_SERVER['HTTP_ORIGIN']) ? parse_url($_SERVER['HTTP_ORIGIN']) : null;
return PlayerRegisterRecord::create([
'player_id' => $id,
'register_domain' => !empty($domain) ? $domain['host'] : null,
'ip' => $ip,
'country_name' => $country_name,
'city_name' => $result['city'] ?? '',
'device' => 'app',
'type' => $type,
'department_id' => $department_id,
]);
}
/**
* 保存头像到本地
* @param $avatar
* @return string
*/
function saveAvatar($avatar): string
{
if (empty($avatar)) {
return '';
}
try {
if (strpos($avatar, 'http://') === 0 || strpos($avatar, 'https://') === 0) {
$client = new Client(['verify' => false]); //忽略SSL错误
$fileName = md5($avatar) . '.jpg';
$path = public_path() . '/storage/avatar/';
if (!is_dir($path) && !mkdir($path, 0777, true)) {
throw new Exception('创建文件夹失败');
}
$client->request('GET', $avatar, ['sink' => public_path('/storage/avatar/' . $fileName)]);
} else {
throw new Exception('网络地址错误');
}
} catch (Exception|GuzzleException $e) {
Log::error('保存头像错误' . $e->getMessage());
return '';
}
return '/storage/avatar/' . $fileName;
}
/**
* 生成唯一单号
* @return string
*/
function createOrderNo(): string
{
$yCode = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
return $yCode[intval(date('Y')) - 2011] . strtoupper(dechex(date('m'))) . date('d') . substr(time(), -5) . substr(microtime(), 2, 5) . sprintf('%02d', rand(0, 99));
}
/**
* 设置短信key
* @param string $phone 手机号
* @param int $type 模式 1 为修改密码短信
* @return string
*/
function setSmsKey(string $phone, int $type): string
{
switch ($type) {
case PhoneSmsLog::TYPE_LOGIN:
return 'sms-login' . $phone;
case PhoneSmsLog::TYPE_REGISTER:
return 'sms-register' . $phone;
case PhoneSmsLog::TYPE_CHANGE_PASSWORD:
return 'sms-change-password' . $phone;
case PhoneSmsLog::TYPE_CHANGE_PAY_PASSWORD:
return 'sms-change-pay-password' . $phone;
case PhoneSmsLog::TYPE_CHANGE_PHONE:
return 'sms-change-phone' . $phone;
case PhoneSmsLog::TYPE_BIND_NEW_PHONE:
return 'sms-type-bind-new-phone' . $phone;
case PhoneSmsLog::TYPE_TALK_BIND:
return 'sms-type-talk-bind' . $phone;
default:
return 'sms-' . $phone;
}
}
/**
* 验证短信
* @param string $country_code 国家编号
* @param string $phone 手机号
* @param string $code 验证码
* @param int $type 类型
* @return string
*/
function verifySMS(string $country_code, string $phone, string $code, int $type): string
{
switch ($country_code) {
case PhoneSmsLog::COUNTRY_CODE_JP:
$phone = ltrim($phone, '0');
break;
case PhoneSmsLog::COUNTRY_CODE_MY:
$phone = ltrim($phone, '0');
break;
default:
break;
}
$phoneCode = Cache::get(setSmsKey($phone, $type));
return $phoneCode == $code;
}
/**
* 获取短信消息
* @param int $type 模式 1 为修改密码短信
* @param string $source 来源
* @return string
*/
function getContent(int $type, string $source): string
{
switch ($type) {
case PhoneSmsLog::TYPE_LOGIN:
return config($source . '-sms.login_content');
case PhoneSmsLog::TYPE_REGISTER:
return config($source . '-sms.register_content');
case PhoneSmsLog::TYPE_CHANGE_PASSWORD:
return config($source . '-sms.change_password_content');
case PhoneSmsLog::TYPE_CHANGE_PAY_PASSWORD:
return config($source . '-sms.change_pay_password');
case PhoneSmsLog::TYPE_CHANGE_PHONE:
return config($source . '-sms.change_phone');
case PhoneSmsLog::TYPE_BIND_NEW_PHONE:
return config($source . '-sms.bind_new_phone');
case PhoneSmsLog::TYPE_TALK_BIND:
return config($source . '-sms.talk_bind');
default:
return config($source . '-sms.sm_content');
}
}
/**
* 提现订单回滚
* @param PlayerWithdrawRecord $playerWithdrawRecord
* @param string $rejectReason
* @param int $withdrawStatus
* @return string
* @throws Exception
*/
function withdrawBack(PlayerWithdrawRecord $playerWithdrawRecord, string $rejectReason = '', int $withdrawStatus = PlayerWithdrawRecord::STATUS_PENDING_REJECT): string
{
DB::beginTransaction();
try {
// 更新提现订单
$playerWithdrawRecord->status = $withdrawStatus;
$playerWithdrawRecord->reject_reason = $rejectReason;
$playerWithdrawRecord->finish_time = date('Y-m-d H:i:s');
$playerWithdrawRecord->user_id = Admin::id() ?? 0;
$playerWithdrawRecord->user_name = !empty(Admin::user()) ? Admin::user()->username : '';
// 更新玩家钱包
$beforeGameAmount = $playerWithdrawRecord->player->wallet->money;
$playerWithdrawRecord->player->wallet->money = bcadd($playerWithdrawRecord->player->wallet->money, $playerWithdrawRecord->coins, 2);
// 跟新玩家统计
$playerWithdrawRecord->player->player_extend->withdraw_amount = bcsub($playerWithdrawRecord->player->player_extend->withdraw_amount, $playerWithdrawRecord->coins, 2);
$playerWithdrawRecord->push();
//寫入金流明細
$playerDeliveryRecord = new PlayerDeliveryRecord;
$playerDeliveryRecord->player_id = $playerWithdrawRecord->player_id;
$playerDeliveryRecord->department_id = $playerWithdrawRecord->department_id;
$playerDeliveryRecord->target = $playerWithdrawRecord->getTable();
$playerDeliveryRecord->target_id = $playerWithdrawRecord->id;
$playerDeliveryRecord->type = PlayerDeliveryRecord::TYPE_WITHDRAWAL_BACK;
$playerDeliveryRecord->source = 'withdraw_back';
$playerDeliveryRecord->amount = $playerWithdrawRecord->coins;
$playerDeliveryRecord->amount_before = $beforeGameAmount;
$playerDeliveryRecord->amount_after = $playerWithdrawRecord->player->wallet->money;
$playerDeliveryRecord->tradeno = $playerWithdrawRecord->tradeno ?? '';
$playerDeliveryRecord->remark = $playerWithdrawRecord->remark ?? '';
$playerDeliveryRecord->save();
DB::commit();
sendSocketMessage('private-recharge_withdrawal', [
'msg_type' => 'withdrawal',
'player_id' => $playerWithdrawRecord->player_id,
'amount' => $playerWithdrawRecord->player->wallet->money,
]);
} catch (\Exception $e) {
DB::rollBack();
throw new Exception($e->getMessage());
}
return true;
}
/**
* 添加渠道财务操作
* @param $target
* @param $action
* @return void
*/
function saveChannelFinancialRecord($target, $action)
{
$channelFinancialRecord = new ChannelFinancialRecord();
$channelFinancialRecord->action = $action;
$channelFinancialRecord->department_id = Admin::user()->department_id ?? 0;
$channelFinancialRecord->player_id = $target->player_id ?? 0;
$channelFinancialRecord->target = $target->getTable();
$channelFinancialRecord->target_id = $target->id;
$channelFinancialRecord->user_id = Admin::id() ?? 0;
$channelFinancialRecord->tradeno = $target->tradeno ?? '';
$channelFinancialRecord->user_name = Admin::user()->username ?? '';
$channelFinancialRecord->save();
}
/**
* 上传base64图片
* @param $img
* @param $path
* @return false|string
*/
function uploadBaseImg($img, $path)
{
if (preg_match('/^(data:\s*image\/(\w+);base64,)/', $img, $result)) {
$type = $result[2];//图片后缀
$savePath = '/storage/' . $path . '/' . date("Ymd", time()) . "/";
$newPath = public_path() . $savePath;
if (!file_exists($newPath)) {
//检查是否有该文件夹,如果没有就创建,并给予最高权限
mkdir($newPath, 0755, true);
}
$filename = time() . '_' . uniqid() . ".{$type}"; //文件名
$newPath = $newPath . $filename;
//写入操作
if (file_put_contents($newPath, base64_decode(str_replace($result[1], '', $img)))) {
return env('APP_URL', 'http://127.0.0.1:8787') . $savePath . $filename;
}
return false;
}
return false;
}
/**
* 检查充值订单取消超时订单
* @throws Exception
*/
function cancelRecharge()
{
/** @var SystemSetting $setting */
$setting = SystemSetting::where('status', 1)->where('feature', 'recharge_order_expiration')->first();
if (!empty($setting)) {
$playerRechargeRecord = PlayerRechargeRecord::where('type', PlayerRechargeRecord::TYPE_REGULAR)
->where('status', PlayerRechargeRecord::STATUS_WAIT)
->where('created_at', '<', Carbon::now()->subMinutes($setting->num))
->get();
/** @var PlayerRechargeRecord $order */
foreach ($playerRechargeRecord as $order) {
$order->status = PlayerRechargeRecord::STATUS_RECHARGED_SYSTEM_CANCEL;
$order->cancel_time = date('Y-m-d H:i:s');
$order->save();
}
}
}
/**
* 发送socket消息
* @param $channels
* @param $content
* @param string $form
* @return bool|string
* @throws PushException
*/
function sendSocketMessage($channels, $content, string $form = 'system')
{
try {
// 发送进入保留状态消息
$api = new Api(
config('plugin.webman.push.app.api'),
config('plugin.webman.push.app.app_key'),
config('plugin.webman.push.app.app_secret')
);
return $api->trigger($channels, 'message', [
'from_uid' => $form,
'content' => json_encode($content)
]);
} catch (Exception $e) {
Log::error('sendSocketMessage', [$e->getMessage()]);
return false;
}
}
/**
* 获取渠道信息
* @param $siteId
* @return array
*/
function getChannel($siteId): array
{
$cacheKey = "channel_" . $siteId;
$channel = Cache::get($cacheKey);
if (empty($channel)) {
$channel = Channel::where('id', $siteId)->whereNull('deleted_at')->first()->toArray();
if (!empty($channel)) {
$cacheKey = "channel_" . $channel->site_id;
Cache::set($cacheKey, $channel->toArray());
} else {
return [];
}
}
return $channel;
}
/**
* 获取堆栈信息
* @return void
*/
function getStackList(): void
{
$line = [];
$debugList = array_reverse(debug_backtrace());
foreach ($debugList as $key => $val) {
$class = $val['class'] ?? "";
$arg = $val['args'];
$parameter = '';
$stringLine = '';
if (!empty($arg) && is_array($arg)) {
foreach ($arg as $v) {
$className = $v;
if (is_object($v)) {
$className = get_class($v);
} elseif (is_array($v)) {
$className = json_encode($v);
}
$parameter .= $className . ',';
}
}
$stringLine .= '程序执行' . $key . ':=>';
$stringLine .= '[1.所在文件(' . $val['file'] . ']';
$stringLine .= '[2.函数调用情况[第' . $val['line'] . '行]' . $class . '->' . $val['function'] . '(' . $parameter . ')]' . "\n";
$line[] = $stringLine;
}
Log::error("堆栈信息", $line);
}
/**
* 获取毫秒级
* @return float
*/
function millisecond(): float
{
list($millisecond, $sec) = explode(' ', microtime());
return (float)sprintf('%.0f', (floatval($millisecond) + floatval($sec)) * 1000);
}
/**
* 创建玩家
* @param $departmentId
* @param $data
* @return Player
* @throws Exception
*/
function createPlayer($departmentId, $data): Player
{
/** @var Channel $channel */
$channel = Channel::where('department_id', $departmentId)->first();
if (empty($channel)) {
throw new Exception(trans('channel_not_found', [], 'message'));
}
DB::beginTransaction();
try {
$count = Player::whereBetween('created_at', [date('Y-m-d') . ' 00:00:00', date('Y-m-d') . ' 23:59:59'])->count('*');
if (empty($data['avatar'])) {
$defAvatars = config('def_avatar') ?? [];
$randomKey = array_rand($defAvatars);
$randomAvatar = $defAvatars[$randomKey] ?? '';
} else {
$randomAvatar = $data['avatar'];
}
$player = new Player();
$player->uuid = gen_uuid();
$player->type = Player::TYPE_PLAYER;
$player->currency = $channel->currency;
$player->department_id = $channel->department_id;
$player->avatar = $randomAvatar;
$player->device_number = $data['device_number'] ?? '';
$player->name = $data['name'] ?? 'channel_' . $departmentId . date('Ymd') . $count;
$player->facebook_id = $data['facebook_id'] ?? '';
$player->phone = $data['phone'] ?? '';
if(isset($data['password'])){
$player->password = $data['password'];
}
if(isset($data['recommend_id'])){
$player->recommend_id = $data['recommend_id'];
}
$player->recommended_code = $data['recommended_code'] ?? '';
$player->status = Player::STATUS_ENABLE;
$player->recommend_code = createCode();
$player->save();
addPlayerExtend($player, [
'email' => $data['email'] ?? ''
]);
addRegisterRecord($player->id, PlayerRegisterRecord::TYPE_CLIENT, $player->department_id);
DB::commit();
} catch (\Exception $e) {
DB::rollBack();
Log::error(trans('register_player_failed', [], 'message') . $e->getMessage());
throw new Exception();
}
unset($player->password);
return $player;
}
/**
* 增加玩家扩展信息
* @param Player $player
* @param array $extend
* @return void
*/
function addPlayerExtend(Player $player, array $extend = [])
{
$registerPresent = SystemSetting::where('feature', 'register_present')->where('status', 1)->value('num') ?? 0;
$playerPlatformCash = new PlayerPlatformCash();
$playerPlatformCash->player_id = $player->id;
$playerPlatformCash->platform_id = PlayerPlatformCash::PLATFORM_SELF;
$playerPlatformCash->money = $registerPresent;
$playerPlatformCash->save();
$playerExtend = new PlayerExtend();
$playerExtend->player_id = $player->id;
$playerExtend->email = $extend['email'];
$playerExtend->save();
if (isset($registerPresent) && $registerPresent > 0) {
//添加玩家钱包日志
$playerMoneyEditLog = new PlayerMoneyEditLog;
$playerMoneyEditLog->player_id = $player->id;
$playerMoneyEditLog->department_id = $player->department_id;
$playerMoneyEditLog->type = PlayerMoneyEditLog::TYPE_INCREASE;
$playerMoneyEditLog->action = PlayerMoneyEditLog::OTHER;
$playerMoneyEditLog->tradeno = date('YmdHis') . rand(10000, 99999);
$playerMoneyEditLog->currency = $player->currency;
$playerMoneyEditLog->money = $registerPresent;
$playerMoneyEditLog->inmoney = $registerPresent;
$playerMoneyEditLog->remark = '';
$playerMoneyEditLog->user_id = Admin::id() ?? 0;
$playerMoneyEditLog->user_name = !empty(Admin::user()) ? Admin::user()->toArray()['username'] : trans('system_automatic', [], 'message');
$playerMoneyEditLog->save();
//寫入金流明細
$playerDeliveryRecord = new PlayerDeliveryRecord;
$playerDeliveryRecord->player_id = $player->id;
$playerDeliveryRecord->department_id = $player->department_id;
$playerDeliveryRecord->target = $playerMoneyEditLog->getTable();
$playerDeliveryRecord->target_id = $playerMoneyEditLog->id;
$playerDeliveryRecord->type = PlayerDeliveryRecord::TYPE_REGISTER_PRESENT;
$playerDeliveryRecord->source = 'register_present';
$playerDeliveryRecord->amount = $playerMoneyEditLog->money;
$playerDeliveryRecord->amount_before = 0;
$playerDeliveryRecord->amount_after = $registerPresent;
$playerDeliveryRecord->tradeno = $playerMoneyEditLog->tradeno ?? '';
$playerDeliveryRecord->remark = $playerMoneyEditLog->remark ?? '';
$playerDeliveryRecord->save();
}
}
/**
* @return Player
* @throws GameException
* @throws PlayerCheckException
* @throws \Exception
*/
function checkPlayer(): Player
{
$departmentId = request()->department_id;
$id = JwtToken::getCurrentId();
/** @var Player $player */
$player = Player::where('id', $id)->where('department_id', $departmentId)->first();
if (empty($player)) {
throw new PlayerCheckException(trans('player_not_fount', [], 'message'), 100);
}
if ($player->status == Player::STATUS_STOP) {
throw new PlayerCheckException(trans('player_stop', [], 'message'), 100);
}
return $player;
}
/**
* 组装请求
* @param string $url
* @param array $params
* @return array|mixed
* @throws Exception
*/
function doCurl(string $url, array $params = [])
{
$response = Http::timeout(7)
->contentType('application/json')
->accept('application/json')
->asJson()
->post($url, $params);
if (!$response->ok()) {
throw new Exception(trans('system_busy', [], 'message'));
}
$data = $response->json();
if (empty($data)) {
throw new Exception(trans('system_busy', [], 'message'));
}
return $data;
}
/**
* 组装请求
* @param $id
* @param array $range
* @return array|mixed|null
*/
function getGamePlayerNum($id, array $range = [])
{
$cacheKey = 'game_player_num_' . $id;
$playerNum = Cache::get($cacheKey);
if ($playerNum === null) {
$playerNum = rand($range[0] ?? 0, $range[1] ?? 0);
Cache::set($cacheKey, $playerNum, 30 * 60);
}
return $playerNum;
}
/**
* 分润结算
* @return void
*/
function commissionSettlement()
{
$date = Carbon::now()->subDay()->format('Y-m-d');
$data = PlayGameRecord::query()
->selectRaw('SUM(bet) - SUM(win) AS damage_amount, player_id, parent_player_id, department_id,sum(deficit) as deficit')
->whereDate('created_at', $date)
->where('status', 0)
->groupBy('player_id', 'parent_player_id', 'department_id')
->get();
Log::info('分润结算开始------------------');
if ($data->isEmpty()) {
return;
}
// 提取父玩家ID
$playerIds = $data->pluck('parent_player_id')->unique()->toArray();
// 获取玩家数据
$players = Player::query()
->with(['player_extend'])
->whereIn('id', $playerIds)
->get()
->keyBy('id');
// 获取系统设置
$systemSettingMap = SystemSetting::query()
->where('status', 1)
->where('feature', 'commission_setting')
->get()
->keyBy('department_id');
$commissionRecord = [];
$unsettledCommissionAmount = [];
foreach ($data->toArray() as $item) {
/** @var SystemSetting $systemSettingData */
//边玩边赚,只返给上级
$systemSettingData = $systemSettingMap->get($item['department_id']);
if(empty($systemSettingData)){
continue;
}
$systemSetting = json_decode($systemSettingData->content, true);
if ($item['deficit'] == 0) {
continue;
}
if ($systemSetting && !empty($systemSetting['commission_damage']) && is_numeric($systemSetting['commission_damage']) && $systemSetting['commission_damage'] > 0) {
$commissionAmount = max(bcmul($item['damage_amount'], bcdiv($systemSetting['commission_damage'], 100, 2), 2), 0);
/** @var Player $player */
$player = $players->get($item['parent_player_id']);
if ($player) {
$commissionRecord[] = [
'player_id' => $item['player_id'],
'department_id' => $item['department_id'],
'parent_player_id' => $item['parent_player_id'],
'recharge_amount' => 0,
'chip_amount' => 0,
'total_amount' => $player->player_extend->commission_amount,
'damage_amount' => $item['damage_amount'],
'amount' => $commissionAmount,
'ratio' => $systemSetting['commission_damage'],
'date' => $date,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
];
if (!isset($unsettledCommissionAmount[$item['parent_player_id']])) {
$unsettledCommissionAmount[$item['parent_player_id']] = $commissionAmount;
} else {
$unsettledCommissionAmount[$item['parent_player_id']] = bcadd($unsettledCommissionAmount[$item['parent_player_id']], $commissionAmount, 2);
}
}
}
}
// 前天发生账变的玩家
$playerList = Player::with(['wallet'])
->whereHas('wallet', function ($query) use ($date) {
$query->where('updated_at', '>=', $date . ' 00:00:00');
})
->get();
// 账变记录
$playerDeliveryRecords = collect(PlayerDeliveryRecord::whereDate('updated_at', '2024-11-26')
->groupBy('player_id')
->whereIn('type', [
PlayerDeliveryRecord::TYPE_MODIFIED_AMOUNT_ADD,
PlayerDeliveryRecord::TYPE_MODIFIED_AMOUNT_DEDUCT,
PlayerDeliveryRecord::TYPE_REGISTER_PRESENT,
PlayerDeliveryRecord::TYPE_GAME_OUT,
PlayerDeliveryRecord::TYPE_GAME_IN,
PlayerDeliveryRecord::TYPE_RECHARGE,
])
->get([
DB::raw("SUM(if(`type`=" . PlayerDeliveryRecord::TYPE_MODIFIED_AMOUNT_ADD . ",`amount`,0)) as admin_add_amount"),
DB::raw("SUM(if(`type`=" . PlayerDeliveryRecord::TYPE_MODIFIED_AMOUNT_DEDUCT . ",`amount`,0)) as admin_deduct_amount"),
DB::raw("SUM(if(`type`=" . PlayerDeliveryRecord::TYPE_REGISTER_PRESENT . ",`amount`,0)) as present_amount"),
DB::raw("SUM(if(`type`=" . PlayerDeliveryRecord::TYPE_RECHARGE . ",`amount`,0)) as recharge_amount"),
'player_id'
])->toArray())
->keyBy('player_id')
->toArray();
// 提现记录
$playerWithdrawRecord = collect(PlayerWithdrawRecord::whereDate('updated_at', $date)
->where('status', PlayerWithdrawRecord::STATUS_SUCCESS)
->groupBy('player_id')
->get([
DB::raw('SUM(`money`) as withdraw_amount'),
'player_id'
]))
->keyBy('player_id')
->toArray();
/** @var Player $player */
foreach ($playerList as $player) {
$change = [
'recharge_amount' => $playerDeliveryRecords[$player->id]['recharge_amount'] ?? 0,
'admin_add_amount' => $playerDeliveryRecords[$player->id]['admin_add_amount'] ?? 0,
'admin_deduct_amount' => $playerDeliveryRecords[$player->id]['admin_deduct_amount'] ?? 0,
'present_amount' => $playerDeliveryRecords[$player->id]['present_amount'] ?? 0,
'bonus_amount' => $playerDeliveryRecords[$player->id]['bonus_amount'] ?? 0,
'withdraw_amount' => $playerWithdrawRecord[$player->id]['withdraw_amount'] ?? 0,
'date' => $date,
];
$change_list[$player->id] = $change;
}
DB::beginTransaction();
try {
$playerUpdates = [];
$playerDeliveryRecord = [];
$promoterProfitRecords = []; // 保存所有的PromoterProfitRecord
foreach ($unsettledCommissionAmount as $key => $amount) {
/** @var Player $player */
$player = $players->get($key);
if ($player) {
$commissionAmount = bcadd($amount, $player->player_extend->unsettled_commission_amount, 2);
if ($commissionAmount > 0) {
$player->player_extend->commission_amount = bcadd($player->player_extend->commission_amount, $commissionAmount, 2);
$player->player_extend->unsettled_commission_amount = 0;
$beforeAmount = $player->wallet->money;
$player->wallet->money = bcadd($player->wallet->money, $commissionAmount, 2);
$playerDeliveryRecord[] = [
'player_id' => $player->id,
'department_id' => $player->department_id,
'target' => 'commission_record',
'target_id' => 0,
'type' => PlayerDeliveryRecord::TYPE_COMMISSION,
'source' => 'commission',
'amount' => $commissionAmount,
'amount_before' => $beforeAmount,
'amount_after' => $player->wallet->money,
'tradeno' => '',
'remark' => '',
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
];
} else {
$player->player_extend->unsettled_commission_amount = $commissionAmount;
}
$playerUpdates[] = $player;
}
}
if (!empty($playerUpdates)) {
/** @var Player $player */
foreach ($playerUpdates as $player) {
$player->push();
}
}
if (!empty($commissionRecord)) {
CommissionRecord::query()->insert($commissionRecord);
}
if (!empty($playerDeliveryRecord)) {
PlayerDeliveryRecord::query()->insert($playerDeliveryRecord);
}
PlayGameRecord::query()
->whereDate('created_at', $date)
->update([
'status' => 1,
'action_at' => date('Y-m-d H:i:s'),
]);
foreach ($data as $value) {
$up_ids = [];
//玩家为代理
if(isset($value->player->player_promoter->path)){
$path = $value->player->player_promoter->path;
$up_ids = explode(',',$path);
//代理不给自己返利
array_pop($up_ids);
}else{
//查询玩家无限上级
if(isset($value->player->recommend_id)){
$path = PlayerPromoter::where('player_id',$value->player->recommend_id)->value('path');
$up_ids = explode(',',$path);
}else{
//未绑定上级的玩家不参与返利计算
continue;
}
}
$up_list = PlayerPromoter::whereIn('player_id',$up_ids)->orderBy('id','asc')->get();
foreach($up_list as $k => &$up_val){
$player_profit_amount = 0;//直属玩家提供的分润
$diff_ratio = 0;//每级代理获得的分润比例
if(isset($up_list[$k+1])){
//上级代理获得的返利比例
$diff_ratio = bcsub($up_val->ratio, $up_list[$k+1]->ratio, 2);
}else{
$diff_ratio = $up_val->ratio;
$player_profit_amount = bcdiv(bcmul($diff_ratio, $value->deficit, 2),100,2);
}
$rebate = bcdiv(bcmul($diff_ratio, $value->deficit, 2),100,2);
//更新推广员信息表
$up_val->player_profit_amount = bcadd($up_val->player_profit_amount,$player_profit_amount,2);//直属玩家提供的分润
$up_val->profit_amount = bcadd($up_val->profit_amount,$rebate,2);//总分润
$team_withdraw_total_amount = isset($change_list[$up_val->player_id]['withdraw_amount']) ? $change_list[$up_val->player_id]['withdraw_amount'] : 0;
$up_val->team_withdraw_total_amount = bcadd($up_val->team_withdraw_total_amount, $team_withdraw_total_amount, 2);
$team_recharge_total_amount = isset($change_list[$up_val->player_id]['recharge_amount']) ? $change_list[$up_val->player_id]['recharge_amount'] : 0;
$up_val->team_recharge_total_amount = bcadd($up_val->team_recharge_total_amount, $team_recharge_total_amount, 2);
$up_val->total_profit_amount = bcadd($up_val->total_profit_amount, $rebate, 2);
$up_val->team_total_profit_amount = bcadd($up_val->team_total_profit_amount, bcmul($value->deficit,bcdiv($up_val->ratio,100,2), 2),2);
$up_val->team_profit_amount = bcadd($up_val->team_profit_amount, bcmul($value->deficit,bcdiv($up_val->ratio,100,2), 2), 2);
$up_val->save();
$promoter_profit_record = [];
$promoter_profit_record['player_id'] = $value->player_id;//玩家id
$promoter_profit_record['department_id'] = $up_val->department_id;
$promoter_profit_record['promoter_player_id'] = $up_val->player_id;//获得分润的id
$promoter_profit_record['source_player_id'] = $value->player->recommend_id;//玩家上级id
$promoter_profit_record['withdraw_amount'] = isset($change_list[$up_val->player_id]['withdraw_amount']) ? $change_list[$up_val->player_id]['withdraw_amount'] : 0;
$promoter_profit_record['recharge_amount'] = isset($change_list[$up_val->player_id]['recharge_amount']) ? $change_list[$up_val->player_id]['recharge_amount'] : 0;
$promoter_profit_record['bonus_amount'] = isset($change_list[$up_val->player_id]['bonus_amount']) ? $change_list[$up_val->player_id]['bonus_amount'] : 0;
$promoter_profit_record['admin_deduct_amount'] = isset($change_list[$up_val->player_id]['admin_deduct_amount']) ? $change_list[$up_val->player_id]['admin_deduct_amount'] : 0;
$promoter_profit_record['admin_add_amount'] = isset($change_list[$up_val->player_id]['admin_add_amount']) ? $change_list[$up_val->player_id]['admin_add_amount'] : 0;
$promoter_profit_record['present_amount'] = isset($change_list[$up_val->player_id]['present_amount']) ? $change_list[$up_val->player_id]['present_amount'] : 0;
$promoter_profit_record['ratio'] = $up_val->ratio;
$promoter_profit_record['actual_ratio'] = $diff_ratio;
$promoter_profit_record['date'] = $date;
$promoter_profit_record['model'] = PromoterProfitRecord::MODEL_TASK;
$promoter_profit_record['profit_amount'] = $rebate;
$promoter_profit_record['player_profit_amount'] = $player_profit_amount;
$created_at = Carbon::now();
$promoter_profit_record['created_at'] = $created_at;
$promoter_profit_record['updated_at'] = $created_at;
$promoterProfitRecords[] = $promoter_profit_record;
}
unset($up_val);
}
PromoterProfitRecord::insert($promoterProfitRecords);
DB::commit();
} catch (\Exception $e) {
DB::rollBack();
Log::error('CommissionSettlement', [$e->getMessage(), $e->getTraceAsString()]);
}
Log::info('分润结算结束------------------');
}
/**
* 客损返水结算
* @return void
*/
function damageRebate()
{
$date = Carbon::now()->subDay()->format('Y-m-d');
$data = CommissionRecord::query()->where('date', $date)->get();
Log::info('客损返水结算------------------');
if ($data->isEmpty()) {
return;
}
// 提取玩家ID
$playerIds = $data->pluck('player_id')->unique()->toArray();
// 获取玩家数据
$players = Player::query()
->with(['wallet', 'player_level'])
->whereIn('id', $playerIds)
->get()
->keyBy('id');
DB::beginTransaction();
try {
/** @var CommissionRecord $item */
foreach ($data as $item) {
/** @var Player $player */
$player = $players->get($item->player_id);
$playerLevel = $player->player_level;
if (!empty($playerLevel) && $playerLevel->damage_rebate_ratio > 0 && $item->damage_amount > 0) {
$damageAmount = bcmul($item->damage_amount, $playerLevel->damage_rebate_ratio, 2);
$beforeGameAmount = $player->wallet->money;
if ($damageAmount > 0) {
// 更新钱包
$player->wallet->money = bcadd($player->wallet->money, $damageAmount, 2);
if ($playerLevel->chip_multiple) {
$chipAmount = bcmul($damageAmount, $playerLevel->chip_multiple);
if ($chipAmount) {
$beforeMustChipAmount = $player->must_chip_amount;
$player->must_chip_amount = bcadd($player->must_chip_amount, $chipAmount, 2);
// 记录打码量明细
$playerChipRecord = new PlayerChipRecord();
$playerChipRecord->player_id = $player->id;
$playerChipRecord->department_id = $player->department_id;
$playerChipRecord->type = PlayerChipRecord::TYPE_INC;
$playerChipRecord->record_type = PlayerChipRecord::RECORD_TYPE_BET_REBATE;
$playerChipRecord->amount = $chipAmount;
$playerChipRecord->chip_amount = 0;
$playerChipRecord->before_chip_amount = $player->chip_amount;
$playerChipRecord->after_chip_amount = $player->chip_amount;
$playerChipRecord->must_chip_amount = $chipAmount;
$playerChipRecord->before_must_chip_amount = $beforeMustChipAmount;
$playerChipRecord->after_must_chip_amount = $player->must_chip_amount;
$playerChipRecord->source_type = CommissionRecord::class;
$playerChipRecord->source_id = $item->id;
$playerChipRecord->save();
}
}
// 根据打码量返水玩家
$playerDeliveryRecord = new PlayerDeliveryRecord;
$playerDeliveryRecord->player_id = $player->id;
$playerDeliveryRecord->department_id = $player->department_id;
$playerDeliveryRecord->target = $item->getTable();
$playerDeliveryRecord->target_id = $item->id;
$playerDeliveryRecord->type = PlayerDeliveryRecord::TYPE_DAMAGE_REBATE;
$playerDeliveryRecord->source = 'damage_rebate';
$playerDeliveryRecord->amount = $damageAmount;
$playerDeliveryRecord->amount_before = $beforeGameAmount;
$playerDeliveryRecord->amount_after = $player->wallet->money;
$playerDeliveryRecord->tradeno = '';
$playerDeliveryRecord->remark = '';
$playerDeliveryRecord->save();
}
}
$player->push();
}
DB::commit();
} catch (\Exception $e) {
DB::rollBack();
Log::error('damageRebate', [$e->getMessage(), $e->getTraceAsString()]);
}
Log::info('客损返水结算结束------------------');
}
/**
* 玩家等级选项
* @return array
*/
function playerLevelOptions(): array
{
$options = [];
for ($i = 0; $i <= 13; $i++) {
if ($i == 0) {
$options[$i] = admin_trans('player.no_level');
} else {
$options[$i] = admin_trans('player.level.' . $i);
}
}
return $options;
}
/**
* 自动生成密码
* @return string
*/
function generateRandomPassword(): string
{
$uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$lowercase = 'abcdefghijklmnopqrstuvwxyz';
$numbers = '0123456789';
$password = '';
$password .= $uppercase[rand(0, strlen($uppercase) - 1)];
$password .= $lowercase[rand(0, strlen($lowercase) - 1)];
$password .= $numbers[rand(0, strlen($numbers) - 1)];
$length = rand(7, 12); // Random length between 7 and 12
$remainingLength = $length - 3;
$allChars = $uppercase . $lowercase . $numbers;
for ($i = 0; $i < $remainingLength; $i++) {
$password .= $allChars[rand(0, strlen($allChars) - 1)];
}
return str_shuffle($password);
}
/**
* 生成用户名
* @param int $length
* @return string
*/
function generateUniqueUsername(int $length = 10): string
{
$characters = 'abcdefghijklmnopqrstuvwxyz0123456789'; // 可选的字符集合
$uniqueUsername = ''; // 初始化唯一用户名
// 生成不重复的用户名
for ($i = 0; $i < $length; $i++) {
$uniqueUsername .= $characters[rand(0, strlen($characters) - 1)];
}
return $uniqueUsername;
}
/**
* 组装请求
* @param string $url
* @param array $params
* @return array|mixed|null
* @throws Exception
*/
function dogGetCurl(string $url, array $params = [])
{
$response = Http::timeout(7)
->contentType('application/json')
->accept('application/json')
->asJson()
->get($url, $params);
if (!$response->ok()) {
throw new Exception(trans('system_busy', [], 'message'));
}
$data = $response->json();
if (empty($data)) {
throw new Exception(trans('system_busy', [], 'message'));
}
return $data;
}
/**
* 设置推广员
* @param $id
* @param $ratio
* @param $name
* @return true
* @throws Exception
*/
function setPromoter($id, $ratio, $name,$recommend_id): bool
{
DB::beginTransaction();
try {
/** @var Player $player */
$player = Player::find($id);
if (empty($player)) {
throw new Exception(trans('player_not_found', [], 'message'));
}
if (!empty($player->player_promoter)) {
throw new Exception(trans('player_is_promoter', [], 'message'));
}
if ($player->status == Player::STATUS_STOP) {
throw new Exception(trans('player_stop', [], 'message'));
}
if($recommend_id != $player->recommend_id){
throw new Exception(trans('not_sub_recommend_player', [], 'message'));
}
$promoter = new PlayerPromoter();
/** @var PlayerPromoter $parentPromoter */
$parentPromoter = PlayerPromoter::where('player_id', $player->recommend_id)->first();
$maxRatio = $parentPromoter->ratio ?? 100;
if ($ratio > $maxRatio) {
throw new Exception(trans('ratio_max_error', ['{max_ratio}' => $maxRatio], 'message'));
}
$orgPromoter = $player->is_promoter;
$path = [];
if (isset($parentPromoter->path) && !empty($parentPromoter->path)) {
$path = explode(',', $parentPromoter->path);
}
$path[] = $player->id;
$promoter->ratio = $ratio;
$promoter->player_id = $player->id;
$promoter->recommend_id = $parentPromoter->player_id ?? 0;
$promoter->department_id = $player->department_id;
$promoter->name = !empty($name) ? $name : $player->name;
$promoter->path = implode(',', $path);
$promoter->save();
// 更新玩家信息
$player->is_promoter = 1;
$player->recommend_code = createCode();
$player->save();
$parentPromoter && $orgPromoter == 0 && $parentPromoter->increment('team_num');
DB::commit();
} catch (\Exception $e) {
DB::rollBack();
throw new Exception($e->getMessage());
}
return true;
}
/**
* 验证手机号格式
* @param $phoneNumber
* @return true
*/
function validateMalaysianPhoneNumber($phoneNumber): bool
{
$pattern = '/^(01\d{8,9}|(\+60|60|0060)(0?1)\d{8,9})$/';
return preg_match($pattern, $phoneNumber);
}
/**
* post表单请求
* @param string $url
* @param array $params
* @return array|mixed
* @throws Exception
*/
function doFormCurl(string $url, array $params = [])
{
$response = Http::timeout(10)->asForm()->post($url, $params);
if (!$response->ok()) {
throw new Exception(trans('system_busy', [], 'message'));
}
$data = $response->json();
if (empty($data)) {
throw new Exception(trans('system_busy', [], 'message'));
}
return $data;
}
/**
* 结算
* @param $id
* @param int $userId
* @param string $userName
* @return void
* @throws Exception
*/
function doSettlement($id, int $userId = 0, string $userName = '')
{
/** @var PlayerPromoter $playerPromoter */
$playerPromoter = PlayerPromoter::where('player_id', $id)->first();
if (empty($playerPromoter)) {
throw new Exception(trans('profit_amount_not_found', [], 'message'));
}
if ($playerPromoter->status == 0) {
throw new Exception(trans('player_promoter_has_disable', [], 'message'));
}
if (!isset($playerPromoter->profit_amount)) {
throw new Exception(trans('profit_amount_not_found', [], 'message'));
}
$profitAmount = PromoterProfitRecord::where('status', PromoterProfitRecord::STATUS_UNCOMPLETED)
->where('promoter_player_id', $id)
->first([
DB::raw('SUM(`withdraw_amount`) as total_withdraw_amount'),
DB::raw('SUM(`recharge_amount`) as total_recharge_amount'),
DB::raw('SUM(`bonus_amount`) as total_bonus_amount'),
DB::raw('SUM(`admin_deduct_amount`) as total_admin_deduct_amount'),
DB::raw('SUM(`admin_add_amount`) as total_admin_add_amount'),
DB::raw('SUM(`present_amount`) as total_present_amount'),
DB::raw('SUM(`profit_amount`) as total_profit_amount'),
DB::raw('SUM(`player_profit_amount`) as total_player_profit_amount'),
])
->toArray();
DB::beginTransaction();
try {
$promoterProfitSettlementRecord = new PromoterProfitSettlementRecord();
$promoterProfitSettlementRecord->department_id = $playerPromoter->player->department_id;
$promoterProfitSettlementRecord->promoter_player_id = $playerPromoter->player_id;
$promoterProfitSettlementRecord->total_withdraw_amount = $profitAmount['total_withdraw_amount'] ?? 0;
$promoterProfitSettlementRecord->total_recharge_amount = $profitAmount['total_recharge_amount'] ?? 0;
$promoterProfitSettlementRecord->total_bonus_amount = $profitAmount['total_bonus_amount'] ?? 0;
$promoterProfitSettlementRecord->total_admin_deduct_amount = $profitAmount['total_admin_deduct_amount'] ?? 0;
$promoterProfitSettlementRecord->total_admin_add_amount = $profitAmount['total_admin_add_amount'] ?? 0;
$promoterProfitSettlementRecord->total_present_amount = $profitAmount['total_present_amount'] ?? 0;
$promoterProfitSettlementRecord->total_profit_amount = $profitAmount['total_profit_amount'] ?? 0;
$promoterProfitSettlementRecord->total_player_profit_amount = $profitAmount['total_player_profit_amount'] ?? 0;
$promoterProfitSettlementRecord->last_profit_amount = $playerPromoter->last_profit_amount;
$promoterProfitSettlementRecord->adjust_amount = $playerPromoter->adjust_amount;
$promoterProfitSettlementRecord->type = PromoterProfitSettlementRecord::TYPE_SETTLEMENT;
$promoterProfitSettlementRecord->tradeno = createOrderNo();
$promoterProfitSettlementRecord->user_id = $userId;
$promoterProfitSettlementRecord->user_name = $userName;
$settlement = $amount = bcadd($promoterProfitSettlementRecord->total_profit_amount,
$promoterProfitSettlementRecord->adjust_amount, 2);
if ($amount > 0) {
if ($playerPromoter->settlement_amount < 0) {
$diffAmount = bcadd($amount, $playerPromoter->settlement_amount, 2);
$settlement = max($diffAmount, 0);
}
}
$promoterProfitSettlementRecord->actual_amount = $settlement;
$promoterProfitSettlementRecord->save();
// 更新结算报表
PromoterProfitRecord::where('status', PromoterProfitRecord::STATUS_UNCOMPLETED)
->where('promoter_player_id', $id)
->update([
'status' => PromoterProfitRecord::STATUS_COMPLETED,
'settlement_time' => date('Y-m-d H:i:s'),
'settlement_tradeno' => $promoterProfitSettlementRecord->tradeno,
'settlement_id' => $promoterProfitSettlementRecord->id,
]);
// 结算后这些数据清零
$playerPromoter->profit_amount = 0;
$playerPromoter->player_profit_amount = 0;
$playerPromoter->team_recharge_total_amount = 0;
$playerPromoter->team_withdraw_total_amount = 0;
$playerPromoter->adjust_amount = 0;
// 更新数据
$playerPromoter->team_profit_amount = bcsub($playerPromoter->team_profit_amount,
$promoterProfitSettlementRecord->total_profit_amount, 2);
$playerPromoter->last_profit_amount = $settlement;
$playerPromoter->settlement_amount = bcadd($playerPromoter->settlement_amount, $amount, 2);
$playerPromoter->team_settlement_amount = bcadd($playerPromoter->team_settlement_amount,
$promoterProfitSettlementRecord->total_profit_amount, 2);
$playerPromoter->last_settlement_time = date('Y-m-d', strtotime('-1 day'));
if (!empty($playerPromoter->path)) {
PlayerPromoter::where('player_id', '!=', $playerPromoter->player_id)
->whereIn('player_id', explode(',', $playerPromoter->path))
->update([
'team_profit_amount' => DB::raw("team_profit_amount - {$promoterProfitSettlementRecord->total_profit_amount}"),
'team_settlement_amount' => DB::raw("team_settlement_amount + $promoterProfitSettlementRecord->total_profit_amount"),
]);
}
if ($settlement > 0) {
// 增加钱包余额
$amountBefore = $playerPromoter->player->wallet->money;
$amountAfter = bcadd($amountBefore, $settlement, 2);
$playerDeliveryRecord = new PlayerDeliveryRecord;
$playerDeliveryRecord->player_id = $playerPromoter->player_id;
$playerDeliveryRecord->department_id = $playerPromoter->department_id;
$playerDeliveryRecord->target = $promoterProfitSettlementRecord->getTable();
$playerDeliveryRecord->target_id = $promoterProfitSettlementRecord->id;
$playerDeliveryRecord->type = PlayerDeliveryRecord::TYPE_PROFIT;
$playerDeliveryRecord->source = 'profit';
$playerDeliveryRecord->amount = $settlement;
$playerDeliveryRecord->amount_before = $amountBefore;
$playerDeliveryRecord->amount_after = $amountAfter;
$playerDeliveryRecord->tradeno = $promoterProfitSettlementRecord->tradeno ?? '';
$playerDeliveryRecord->remark = '';
$playerDeliveryRecord->save();
$playerPromoter->player->wallet->money = $amountAfter;
}
$playerPromoter->push();
DB::commit();
} catch (\Exception $e) {
DB::rollback();
throw new Exception($e->getMessage());
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace app\middleware;
use Webman\Http\Request;
use Webman\Http\Response;
use Webman\MiddlewareInterface;
class AccessControl implements MiddlewareInterface
{
public function process(Request $request, callable $handler): Response
{
// 如果是opitons请求则返回一个空的响应否则继续向洋葱芯穿越并得到一个响应
$response = $request->method() == 'OPTIONS' ? response('') : $handler($request);
// 给响应添加跨域相关的http头
$response->withHeaders([
'Access-Control-Allow-Credentials' => 'true',
'Access-Control-Allow-Origin' => $request->header('origin', '*'),
'Access-Control-Allow-Methods' => $request->header('access-control-request-method', '*'),
'Access-Control-Allow-Headers' => $request->header('access-control-request-headers', '*'),
]);
return $response;
}
}

View File

@@ -0,0 +1,57 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace app\middleware;
use addons\webman\model\AppVersion;
use Webman\Http\Request;
use Webman\Http\Response;
use Webman\MiddlewareInterface;
/**
* 站点验证中间件
* Class SiteAuthMiddleware
* @package app\middleware
*/
class AppVersionMiddleware implements MiddlewareInterface
{
public function process(Request $request, callable $handler): Response
{
// 更新比较时,查询的是版本标识,大于传入的版本标识时,返回新版本的数据
$versionKey = $request->header('app-version-key');
$systemKey = $request->header('system-key');
// 传入检测的系统
if (empty($versionKey) || empty($systemKey)) {
return jsonFailResponse('缺少版本号或版本标识');
}
$data = AppVersion::query()
->where('app_version_key', '>', $versionKey)
->where(['status' => 1])
->where('status', 1)
->where('force_update', 1)
->where('system_key', $systemKey)
->where('department_id', request()->department_id)
->whereDate('regular_update', '<', date("Y-m-d H:i:s", time()))
->select(['id', 'system_key', 'app_version', 'app_version_key', 'apk_url', 'hot_update_url', 'force_update', 'hot_update', 'regular_update', 'update_content', 'notes'])
->orderBy('id', 'desc')
->first();
if (!empty($data)) {
return jsonFailResponse('有新版本', [
'data' => $data
], 406);
}
return $handler($request);
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace app\middleware;
use addons\webman\model\ExternalApp;
use Tinywan\Jwt\JwtToken;
use Webman\Http\Request;
use Webman\Http\Response;
use Webman\MiddlewareInterface;
class ExternalAppMiddleware implements MiddlewareInterface
{
/**
* @throws \Exception
*/
public function process(Request $request, callable $handler): Response
{
try {
$id = JwtToken::getCurrentId();
// 验证授权应用
/** @var ExternalApp $externalApp */
$externalApp = ExternalApp::where('id', base64_decode($id))->whereNull('deleted_at')->where('status', 1)->first();
if (empty($externalApp)) {
throw new \Exception('应用不存在');
}
// 验证服务器ip
if (!empty($externalApp->white_ip) && !in_array(request()->getRealIp(), explode(',', $externalApp->white_ip))) {
throw new \Exception('IP认证不通过');
}
} catch (\Exception $e) {
return jsonFailResponse($e->getMessage(), [], 0);
}
return $handler($request);
}
}

17
app/middleware/Lang.php Normal file
View File

@@ -0,0 +1,17 @@
<?php
namespace app\middleware;
use Illuminate\Support\Str;
use Webman\Http\Request;
use Webman\Http\Response;
use Webman\MiddlewareInterface;
class Lang implements MiddlewareInterface
{
public function process(Request $request, callable $handler): Response
{
$lang = $request->header('Lang') ?? 'zh_CN';
locale(session('lang', Str::replace('-', '_', $lang)));
return $handler($request);
}
}

View File

@@ -0,0 +1,61 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace app\middleware;
use addons\webman\model\Channel;
use support\Cache;
use Webman\Http\Request;
use Webman\Http\Response;
use Webman\MiddlewareInterface;
/**
* 站点验证中间件
* Class SiteAuthMiddleware
* @package app\middleware
*/
class SiteAuthMiddleware implements MiddlewareInterface
{
public function process(Request $request, callable $handler): Response
{
// 站点标识
$siteId = $request->header('Site-Id');
// 排除接口
if ($request->path() == '/api/v1/talk-pay-notify') {
return $handler($request);
}
if (empty($siteId)) {
return response('fail', 400);
}
$cacheKey = "channel_" . $siteId;
$channel = Cache::get($cacheKey);
if (empty($channel)) {
/** @var Channel $channel */
$channel = Channel::where('site_id', $siteId)->whereNull('deleted_at')->first();
if (!empty($channel)) {
$cacheKey = "channel_" . $channel->site_id;
Cache::set($cacheKey, $channel->toArray());
} else {
return response('fail', 400);
}
}
if ($channel['status'] == 0 || !empty($channel['deleted_at'])) {
return response('fail', 400);
}
$request->department_id = $channel['department_id'];
$request->site_id = $siteId;
return $handler($request);
}
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace app\middleware;
use Webman\Http\Request;
use Webman\Http\Response;
use Webman\MiddlewareInterface;
/**
* Class StaticFile
* @package app\middleware
*/
class StaticFile implements MiddlewareInterface
{
public function process(Request $request, callable $next): Response
{
// Access to files beginning with. Is prohibited
if (strpos($request->path(), '/.') !== false) {
return response('<h1>403 forbidden</h1>', 403);
}
/** @var Response $response */
$response = $next($request);
// Add cross domain HTTP header
/*$response->withHeaders([
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Credentials' => 'true',
]);*/
return $response;
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace app\queue\redis;
use addons\webman\model\PlayerRechargeRecord;
use ExAdmin\ui\traits\queueProgress;
use think\Exception;
use Webman\RedisQueue\Consumer;
class CancelRecharge implements Consumer
{
use queueProgress;
public $queue = 'cancel_recharge';
public $connection = 'default';
/**
* @param $data
* @throws Exception
*/
public function consume($data)
{
/** @var PlayerRechargeRecord $playerRechargeRecord */
$playerRechargeRecord = PlayerRechargeRecord::where('type', PlayerRechargeRecord::TYPE_REGULAR)
->where('status', PlayerRechargeRecord::STATUS_WAIT)
->where('id', $data['id'])
->first();
if (!empty($playerRechargeRecord)) {
$playerRechargeRecord->status = PlayerRechargeRecord::STATUS_RECHARGED_SYSTEM_CANCEL;
$playerRechargeRecord->cancel_time = date('Y-m-d H:i:s');
$playerRechargeRecord->save();
}
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace app\queue\redis;
use support\Log;
use Webman\Push\PushException;
use Webman\RedisQueue\Consumer;
class SendBroadcasts implements Consumer
{
// 要消费的队列名
public $queue = 'broadcast_tasks';
// 连接名,对应 plugin/webman/redis-queue/redis.php 里的连接`
public $connection = 'default';
// 消费
public function consume($data)
{
// 无需反序列化
try {
sendSocketMessage('broadcast', $data);
} catch (PushException $e) {
Log::error($e->getMessage());
}
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace app\service;
/**
* 发送短信接口
*/
interface BaseSmsServices
{
/**
* 发送短信
* @param string $phone
* @param int $type
* @return mixed
*/
public function send(string $phone, int $type);
}

158
app/service/DrawService.php Normal file
View File

@@ -0,0 +1,158 @@
<?php
// 抽奖核心服务
namespace app\service;
use addons\webman\model\DrawRecord;
use addons\webman\model\Game;
use addons\webman\model\Prize;
use Exception;
use support\Db;
use support\Log;
class DrawService
{
/**
* 执行抽奖
* @param $player
* @param int $gameId
* @param int $departmentId
* @param string $ip 用户IP
* @return array 抽奖结果
*/
public function execute( $player, int $gameId, int $departmentId, string $ip): array
{
try {
DB::beginTransaction();
// 1. 查询有效奖品(加行锁防止并发问题)
$prizes = Prize::query()->where('game_id', $gameId)
->where('department_id', $departmentId)
->where('status', 1)
->where('total_remaining', '>', 0)
->where('daily_remaining', '>', 0)
->lockForUpdate()
->get()
->toArray();
if (empty($prizes)) {
DB::rollBack();
return [
'success' => false,
'message' => '当前游戏不可用'
];
}
$game = Game::query()->select('id', 'game_type', 'consume')->where('id', $gameId)->first()->toArray();
// 2. 计算中奖结果
$result = $this->calculateWinning($player,$prizes, $game['consume']);
$prizeId = $result['prize_id'];
$prizeName = $result['prize_name'];
$prizeType = $result['prize_type'];
$game['department_id'] = $departmentId;
// 3. 记录抽奖信息
$this->createRecord($player->id, $result, $game, $ip);
if ($result['prize_type'] == 3) {
$message = '很遗憾,未中奖';
} else {
$message = '恭喜获得' . $prizeName;
}
DB::commit();
return [
'prize_id' => $prizeId,
'prize_name' => $prizeName,
'prize_type' => $prizeType,
'consume' => $game['consume'],
'message' => $message
];
} catch (Exception $e) {
DB::rollBack();
Log::error("抽奖失败:{$e->getMessage()}", [
'user_id' => $player->id,
'trace' => $e->getTraceAsString()
]);
return [
'success' => false,
'message' => '抽奖过程异常,请稍后再试'
];
}
}
/**
* 计算中奖结果
* @param array $prizes 有效奖品列表
* @return array 中奖信息
* @throws Exception
*/
private function calculateWinning($player, array $prizes, $consume): array
{
// 计算总概率权重
$totalProb = array_sum(array_column($prizes, 'probability'));
if ($totalProb <= 0) {
return ['prize_id' => null, 'type' => Prize::PRIZE_TYPE_LOSE, 'prize_name' => '未中奖', 'prize_pic' => null];
}
$player->wallet->decrement('money', $consume);
// 生成随机数0到总权重之间
$random = mt_rand(1, $totalProb * 10000) / 10000; // 提高精度
$currentSum = 0;
foreach ($prizes as $prize) {
$currentSum += $prize['probability'];
if ($random <= $currentSum) {
// 检查并扣减库存
$this->deductStock($prize['id']);
return [
'prize_id' => $prize['id'],
'prize_type' => $prize['type'],
'prize_name' => $prize['name'],
'prize_pic' => $prize['pic']
];
}
}
return ['prize_id' => null, 'type' => Prize::PRIZE_TYPE_LOSE, 'prize_name' => '未中奖', 'prize_pic' => null];
}
/**
* 扣减库存
* @param int $prizeId 奖品ID
* @throws Exception
*/
private function deductStock(int $prizeId): void
{
$prize = Prize::query()->findOrFail($prizeId);
// 再次检查库存(防止并发超卖)
if ($prize->total_remaining <= 0 || $prize->daily_remaining <= 0) {
throw new \Exception("奖品库存不足");
}
$prize->decrement('total_remaining');
$prize->decrement('daily_remaining');
$prize->save();
}
/**
* 创建抽奖记录
* @param int $userId 用户ID
* @param array|null $prize 奖品
* @param string $ip IP地址
*/
private function createRecord(int $userId, ?array $prize, array $game, string $ip): void
{
DrawRecord::query()->create([
'uid' => $userId,
'prize_id' => $prize['prize_id'],
'prize_type' => $prize['prize_type'],
'prize_name' => $prize['prize_name'],
'prize_pic' => $prize['prize_pic'],
'game_id' => $game['id'],
'consume' => $game['consume'],
'game_type' => $game['game_type'],
'department_id' => $game['department_id'],
'draw_time' => date('Y-m-d H:i:s'),
'ip' => $ip
]);
}
}

View File

@@ -0,0 +1,103 @@
<?php
// 日本手机号发送短信
namespace app\service;
use addons\webman\model\PhoneSmsLog;
use Exception;
use Illuminate\Support\Str;
use support\Cache;
use WebmanTech\LaravelHttpClient\Facades\Http;
class JpSmsServicesServices implements BaseSmsServices
{
// 应用key
private $appKey = '';
// 应用代码
private $appcode = '';
// 应用秘钥
private $appSecret = '';
// domain
private $domain = '';
// 过期时间
public $expireTime = 120;
public function __construct()
{
$this->domain = config('jp-sms.domain');
$this->appKey = config('jp-sms.app_key');
$this->appcode = config('jp-sms.appcode');
$this->appSecret = config('jp-sms.app_secret');
}
/**
* 执行请求
* @param string $api 接口
* @param array $params 参数
* @return mixed
*/
public function doCurl(string $api, array $params)
{
$result = Http::timeout(10)->get($this->domain . $api, $params);
return json_decode($result, true);
}
/**
* 日本供应商短信发送
* @param $phone
* @param $type
* @param int $playerId
* @return bool
* @throws Exception
*/
public function send($phone, $type, int $playerId = 0): bool
{
$env = config('app.env');
$api = config('jp-sms.batchSend');
$code = ($env == 'pro' ? random_int(10000, 99999) : config('sms.default_code'));
$key = setSmsKey($phone, $type);
$uid = gen_uuid();
$msg = Str::replaceFirst('{code}', $code, getContent($type, 'jp'));
//驗證通過
if ($env == 'pro') {
$result = $this->doCurl($api, [
'appKey' => $this->appKey,
'appcode' => $this->appcode,
'appSecret' => $this->appSecret,
'uid' => $uid,
'phone' => PhoneSmsLog::COUNTRY_CODE_JP . $phone,
'msg' => $msg
]);
} else {
$result = $env;
}
$phoneSmsLog = new PhoneSmsLog();
$phoneSmsLog->player_id = $playerId;
$phoneSmsLog->code = $code;
$phoneSmsLog->phone = PhoneSmsLog::COUNTRY_CODE_JP . $phone;
$phoneSmsLog->uid = $uid;
$phoneSmsLog->send_times = 1;
$phoneSmsLog->type = $type;
$phoneSmsLog->expire_time = date("Y-m-d H:i:s", time() + $this->expireTime);
$phoneSmsLog->response = $result ? json_encode($result) : '';
if ($env == 'pro') {
if (isset($result) && $result['code'] == '00000') {
if (isset($result['result']) && $result['result'][0]['status'] == '00000') {
Cache::set($key, $code, $this->expireTime);
$phoneSmsLog->status = 1;
$phoneSmsLog->save();
return true;
}
}
} else {
Cache::set($key, $code, $this->expireTime);
$phoneSmsLog->status = 1;
$phoneSmsLog->save();
return true;
}
$phoneSmsLog->status = 0;
$phoneSmsLog->save();
throw new Exception(trans('phone_code_send_failed', [], 'message'));
}
}

View File

@@ -0,0 +1,147 @@
<?php
// 日本手机号发送短信
namespace app\service;
use app\exception\GameException;
use support\Log;
use WebmanTech\LaravelHttpClient\Facades\Http;
class OnePayServices
{
// 应用key
private $merchantId = '';
// 应用代码
private $key = '';
// 应用秘钥
private $domain = '';
// domain
private $notifyUrl = '';
private $payoutNotifyUrl = '';
private $returnUrl = '';
// 过期时间
public $expireTime = 180;
public function __construct()
{
$this->merchantId = config('one_pay.merchantId');
$this->key = config('one_pay.key');
$this->api_key = config('one_pay.api_key');
$this->domain = config('one_pay.domain');
$this->notifyUrl = config('one_pay.notifyUrl');
$this->payoutNotifyUrl = config('one_pay.payoutNotifyUrl');
$this->returnUrl = config('one_pay.returnUrl');
}
/**
* 执行请求
* @param string $api 接口
* @param array $params 参数
*/
public function doCurl(string $api, array $params): array
{
$result = Http::timeout(10)->asForm()->post($this->domain.$api, $params);
Log::channel('pay_log')->info('onepay:'.$api,json_decode($result, true));
return json_decode($result, true);
}
/**
* 创建签名
* @throws GameException
*/
public function getAuth(): string
{
$data = [
'username' => $this->merchantId,
'api_key' => $this->api_key,
];
$result = $this->doCurl('merchant/auth',$data);
if ($result['status'] == 'true') {
return $result['auth'];
} else {
throw new GameException($result['message'], 0);
}
}
/**
* 创建签名
* @param array $params 参数
*/
public function calculateSignature(array $params): string
{
ksort($params);
$signature_string = '';
foreach ($params as $key => $value) {
$signature_string .= $key . ':' . $value . '&';
}
$signature_string .= 'key:' . $this->key;
return strtoupper(md5($signature_string));
}
/**
* 验证返回签名
* @param array $params 参数
*/
public function verifySign(array $params): string
{
return md5($this->key.$params['order_id']);
}
/**
* 存款
* @param array $params 参数
* @throws GameException
*/
public function deposit(array $params): array
{
$data = [
'username' => $params['uuid'],
'auth' => $this->getAuth(),
'amount' => $params['amount'],
'currency' => 'MYR',
'orderid' => $params['orderNo'],
'email' => $params['email'],
'phone_number' => $params['phone'],
'redirect_url' => $this->returnUrl.'?orderNo='.$params['orderNo'],
'pay_method' => $params['paymentCode'],
'callback_url' => $this->notifyUrl,
];
if ($data['pay_method'] == 'online_banking') {
$data['bank_id'] = $params['bank_id'];
}
return $this->doCurl('merchant/generate_orders',$data);
}
/**
* 代付
* @param array $params 参数
* @throws GameException
*/
public function payout(array $params): array
{
$data = [
'auth' => $this->getAuth(),
'amount' => $params['amount'],
'currency' => 'MYR',
'orderid' => $params['orderNo'],
'bank_id' => $params['bankCode'],
'holder_name' => $params['bankAccountName'],
'account_no' => $params['bankAccountNo'],
'callback_url' => $this->payoutNotifyUrl,
];
return $this->doCurl('merchant/withdraw_orders',$data);
}
/**
* 订单查询
* @param array $params 参数
*/
public function query(array $params): array
{
$data = [
'username' => $this->merchantId,
'id' => $params['orderNo'],
];
return $this->doCurl('merchant/check_status',$data);
}
}

View File

@@ -0,0 +1,130 @@
<?php
// 日本手机号发送短信
namespace app\service;
use support\Log;
use WebmanTech\LaravelHttpClient\Facades\Http;
class SePayServices
{
// 应用key
private $merchantId = '';
// 应用代码
private $key = '';
// 应用秘钥
private $domain = '';
// domain
private $notifyUrl = '';
private $payoutNotifyUrl = '';
private $returnUrl = '';
// 过期时间
public $expireTime = 180;
public function __construct()
{
$this->merchantId = config('se-pay.merchantId');
$this->key = config('se-pay.MD5');
$this->domain = config('se-pay.domain');
$this->notifyUrl = config('se-pay.notifyUrl');
$this->payoutNotifyUrl = config('se-pay.payoutNotifyUrl');
$this->returnUrl = config('se-pay.returnUrl');
}
/**
* 执行请求
* @param string $api 接口
* @param array $params 参数
*/
public function doCurl(string $api, array $params): array
{
$result = Http::timeout(10)->asForm()->post($this->domain.$api, $params);
Log::channel('pay_log')->info('sepay:'.$api,json_decode($result, true));
return json_decode($result, true);
}
/**
* 创建签名
* @param array $params 参数
*/
public function calculateSignature(array $params): string
{
ksort($params);
$signature_string = '';
foreach ($params as $key => $value) {
$signature_string .= $key . ':' . $value . '&';
}
$signature_string .= 'key:' . $this->key;
return strtoupper(md5($signature_string));
}
/**
* 验证返回签名
* @param array $params 参数
*/
public function verifySign(array $params): string
{
ksort($params);
unset($params['signMsg']);
$signature_string = '';
foreach ($params as $key => $value) {
$signature_string .= $key . ':' . $value . '&';
}
$signature_string .= 'key:' . $this->key;
return strtoupper(md5($signature_string));
}
/**
* 存款
* @param array $params 参数
*/
public function deposit(array $params): array
{
$params['notifyUrl'] = $this->notifyUrl;
$params['returnUrl'] = $this->returnUrl.'?orderNo='.$params['orderNo'];
$request_str = base64_encode(json_encode($params));
$data = [
'merchantNo' => $this->merchantId,
'request' => $request_str,
'version' => '1.0',
];
$signature = $this->calculateSignature($data);
$data['signMsg'] = $signature;
return $this->doCurl('qrh5',$data);
}
/**
* 代付
* @param array $params 参数
*/
public function payout(array $params): array
{
$params['notifyUrl'] = $this->payoutNotifyUrl;
$request_str = base64_encode(json_encode($params));
$data = [
'merchantNo' => $this->merchantId,
'request' => $request_str,
'version' => '1.0',
];
$signature = $this->calculateSignature($data);
$data['signMsg'] = $signature;
return $this->doCurl('payout',$data);
}
/**
* 订单查询
* @param array $params 参数
*/
public function query(array $params): array
{
$request_str = base64_encode(json_encode($params));
$data = [
'merchantNo' => $this->merchantId,
'request' => $request_str,
'version' => '1.0',
];
$signature = $this->calculateSignature($data);
$data['signMsg'] = $signature;
return $this->doCurl('query',$data);
}
}

View File

@@ -0,0 +1,96 @@
<?php
// 日本手机号发送短信
namespace app\service;
use app\exception\GameException;
use support\Log;
use WebmanTech\LaravelHttpClient\Facades\Http;
class SklPayServices
{
// 应用key
private $merchantId = '';
// 应用代码
private $api_key = '';
// 应用秘钥
private $domain = '';
// domain
private $notifyUrl = '';
private $payoutNotifyUrl = '';
private $returnUrl = '';
// 过期时间
public $expireTime = 180;
/**
* @var array|mixed|null
*/
public function __construct()
{
$this->merchantId = config('skl_pay.merchantId');
$this->api_key = config('skl_pay.api_key');
$this->domain = config('skl_pay.domain');
$this->notifyUrl = config('skl_pay.notifyUrl');
$this->payoutNotifyUrl = config('skl_pay.payoutNotifyUrl');
$this->returnUrl = config('skl_pay.returnUrl');
}
/**
* 执行请求
* @param string $api 接口
* @param array $params 参数
*/
public function doCurl(string $api, array $params): array
{
$result = Http::timeout(10)->asJson()->post($this->domain.$api, $params);
Log::channel('pay_log')->info('sklpay:'.$api,json_decode($result, true));
return json_decode($result, true);
}
/**
* 存款
* @param array $params 参数
* @throws GameException
*/
public function deposit(array $params): array
{
$data = [
'api_token' => $this->api_key,
'amount' => $params['amount'],
'gateway' => $params['paymentCode'],
'pusername' => $params['name'],
'invoice_no' => $params['orderNo'],
];
return $this->doCurl('/api/transaction/init',$data);
}
/**
* 代付
* @param array $params 参数
* @throws GameException
*/
public function payout(array $params): array
{
$data = [
'api_token' => $this->api_key,
'amount' => $params['amount'],
'to_bank' => $params['bankCode'],
'to_bank_account_no' => $params['bankAccountNo'],
'account_holder' => $params['bankAccountName'],
'invoice_no' => $params['orderNo'],
];
return $this->doCurl('/api/transfer_out/init',$data);
}
/**
* 订单查询
* @param array $params 参数
*/
public function query(array $params): array
{
$data = [
'transaction_id' => $params['orderNo'],
];
return $this->doCurl('/api/transaction/get_status',$data);
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace app\service;
use addons\webman\model\PhoneSmsLog;
use Exception;
/**
* 短信服务
*/
class SmsServicesServices
{
/**
* @param int $countryCode
* @param string $phone
* @param int $type
* @param int $playerId
* @param string $name
* @return bool
* @throws Exception
*/
public static function sendSms(int $countryCode, string $phone, int $type, int $playerId = 0, string $name = ''): bool
{
$openCountryCode = config('sms.open_country_code');
if (!in_array($countryCode, $openCountryCode)) {
$openCountryName = '';
foreach ($openCountryCode as $item) {
$openCountryName .= ',' . trans('country_code_name.' . $item, [], 'message');
}
throw new Exception(trans('currently_open_countries_and_regions', ['{openCountryCode}', $openCountryName], 'message'));
}
switch ($countryCode) {
case PhoneSmsLog::COUNTRY_CODE_TW:
return (new TwSmsServicesServices())->send($phone, $type, $playerId, $name);
case PhoneSmsLog::COUNTRY_CODE_JP:
return (new JpSmsServicesServices())->send($phone, $type, $playerId);
default:
throw new Exception(trans('country_code_error', [], 'message'));
}
}
}

View File

@@ -0,0 +1,121 @@
<?php
// 台湾手机号发送短信
namespace app\service;
use addons\webman\model\PhoneSmsLog;
use Exception;
use Illuminate\Support\Str;
use support\Cache;
use support\Log;
use WebmanTech\LaravelHttpClient\Facades\Http;
class TwSmsServicesServices implements BaseSmsServices
{
// 使⽤者帳號
private $username = '';
// 使⽤者密碼
private $password = '';
// domain
private $domain = '';
// 过期时间
public $expireTime = 300;
public function __construct()
{
$this->domain = config('tw-sms.domain');
$this->username = config('tw-sms.username');
$this->password = config('tw-sms.password');
}
/**
* 执行请求
* @param string $api 接口
* @param array $params 参数
* @return mixed
* @throws Exception
*/
public function doCurl(string $api, array $params)
{
$result = Http::timeout(10)->post($this->domain . $api . '?' . http_build_query($params));
if ($result->ok()) {
$arr = preg_split('/[;\r\n]+/s', $result->body());
$data = [];
foreach ($arr as $item) {
$arr = explode('=', $item);
if (!empty($arr) && isset($arr[0]) && isset($arr[1])) {
$data[$arr[0]] = $arr[1];
}
}
return $data;
}
throw new Exception(trans('phone_code_send_failed', [], 'message'));
}
/**
* 日本供应商短信发送
* @param $phone
* @param $type
* @param int $playerId
* @param string $name
* @return bool
* @throws Exception
*/
public function send($phone, $type, int $playerId = 0, string $name = ''): bool
{
$env = config('app.env');
$api = config('tw-sms.sm_send_api');
$code = ($env == 'pro' ? random_int(10000, 99999) : config('sms.default_code'));
$key = setSmsKey($phone, $type);
$uid = gen_uuid();
$msg = Str::replaceFirst('{code}', $code, getContent($type, 'tw'));
//驗證通過
if ($env == 'pro') {
$result = $this->doCurl($api, [
'username' => $this->username,
'password' => $this->password,
'dstaddr' => $phone,
'destname' => $name,
'dlvtime' => '',
'vldtime' => $this->expireTime,
'smbody' => $msg,
'CharsetURL' => 'UTF-8',
]);
Log::info('短信发送结果', [$result]);
} else {
$result = $env;
}
$phoneSmsLog = new PhoneSmsLog();
$phoneSmsLog->player_id = $playerId;
$phoneSmsLog->code = $code;
$phoneSmsLog->phone = PhoneSmsLog::COUNTRY_CODE_TW . $phone;
$phoneSmsLog->uid = $uid;
$phoneSmsLog->send_times = 1;
$phoneSmsLog->type = $type;
$phoneSmsLog->expire_time = date("Y-m-d H:i:s", time() + $this->expireTime);
$phoneSmsLog->response = $result ? json_encode($result) : '';
if ($env == 'pro') {
if (isset($result['statuscode'])) {
/* 0 預約傳送中1 已送達業者2 已送達業者4 已送達⼿機5 內容有錯誤6 ⾨號有錯誤7 簡訊已停⽤8 逾時無送達9 預約已取消*/
switch ($result['statuscode']) {
case '0':
case '1':
case '2':
case '4':
Cache::set($key, $code, $this->expireTime);
$phoneSmsLog->status = 1;
$phoneSmsLog->save();
return true;
}
}
} else {
Cache::set($key, $code, $this->expireTime);
$phoneSmsLog->status = 1;
$phoneSmsLog->save();
return true;
}
$phoneSmsLog->status = 0;
$phoneSmsLog->save();
throw new Exception(trans('phone_code_send_failed', [], 'message'));
}
}

View File

@@ -0,0 +1,759 @@
<?php
namespace app\service\game;
use addons\webman\model\Game;
use addons\webman\model\GamePlatform;
use addons\webman\model\GameType;
use addons\webman\model\Player;
use addons\webman\model\PlayerGamePlatform;
use addons\webman\model\PlayerWalletTransfer;
use app\exception\GameException;
use DateTime;
use DateTimeZone;
use Exception;
use Illuminate\Support\Str;
use support\Log;
use support\Response;
class BigGamingServiceInterface extends GameServiceFactory implements GameServiceInterface
{
public $method = 'POST';
private $apiDomain;
private $domain;
private $appId;
private $appSecret;
private $loginId;
private $sn;
private $adminUser;
private $secretCode;
public $gameType = [
'2' => 'Casino',
'5' => 'Fishing',
'8' => 'Bingo',
'1' => 'Slot',
];
public $localGameType = [
'2' => '2',//赌场
'4' => '4',//捕鱼
'5' => '5',//真人视讯
];
public $failCode = [
'S200' => '重復請求',
'F0001' => '無效的簽名',
'F0002' => '無效的SN',
'F0003' => '無效的參數',
'F0004' => '無效的貨幣',
'F0005' => '玩家已存在',
'F0006' => '玩家不存在',
'F0007' => '會員不存在',
'F0008' => '執行失敗',
'F0009' => '無效的方法',
'F0010' => '無效的用戶狀態',
'F0011' => '玩家狀態無需更新',
'F0012' => '超出數據範圍',
'F0013' => '無匹配數據',
'F0014' => '登入位置被禁止',
'F0015' => '分數不足夠',
'F0016' => '不支持禮碼',
'F0017' => '交易流水號不得重複',
'F0018' => '系統繁忙',
'F0019' => '日期時間各式錯誤',
'F0020' => '超出時間限制範圍開始時間與結束時間之間不能大於120分鐘',
'F0021' => '執行取消',
'M0001' => '系統維護',
'M0002' => '系統錯誤',
];
/**
* @param Player|null $player
* @param $type
* @throws Exception
*/
public function __construct($type, Player $player = null)
{
$config = config('game_platform.' . $type);
$this->appId = $config['app_id'];
$this->apiDomain = $config['api_domain'];
$this->domain = $config['domain'];
$this->appSecret = $config['app_secret'];
$this->sn = $config['sn'];
$this->adminUser = $config['admin_user'];
$this->secretCode = base64_encode(sha1($config['admin_pass'], true));
$this->platform = GamePlatform::query()->where('name', $type)->first();
if (!empty($player)) {
$this->player = $player;
$this->getLoginId();
}
}
/**
* 生成请求url
* @return string
*/
public function createUrl(): string
{
return $this->apiDomain;
}
//生成签名
public function createSign($params): string
{
$str = '';
foreach ($params as $v) {
$str .= $v;
}
return md5($str);
}
/**
* 生成请求数据
* @param $postData
* @param $method
* @return array
*/
public function buildParams($postData, $method): array
{
return array(
"jsonrpc" => "2.0",
"method" => $method,
"params" => $postData,
"id" => $this->player->uuid ?? uniqid()
);
}
/**
* 创建代理账号
* @throws GameException|\think\Exception
*/
public function createAgent()
{
$params = [
'random' => $this->player->uuid ?? uniqid(),
'sn' => $this->sn,
'loginId' => 'testagent123',
'secretKey' => $this->appSecret,
];
$params['sign'] = $this->createSign($params);
unset($params['secretKey']);
$params['password'] = '123456ss';
$postData = $this->buildParams($params, 'open.agent.create');
$result = doCurl($this->apiDomain,$postData);
if (!empty($result['result']) && empty($result['error'])){
return $result;
}else{
throw new GameException($result['error']['message'], 0);
}
}
/**
* 更新游戏列表
* @return false
*/
public function getSimpleGameList(): bool
{
return false;
}
/**
* 获取玩家ID
* @throws Exception
*/
protected function getLoginId()
{
/** @var PlayerGamePlatform $playerGamePlatform */
$playerGamePlatform = PlayerGamePlatform::query()->where('platform_id', $this->platform->id)->where('player_id',$this->player->id)->first();
if (!empty($playerGamePlatform)) {
return $this->loginId = $playerGamePlatform->player_code;
}
return $this->createPlayer([
'uuid' => $this->player->uuid,
'name' => $this->player->name,
]);
}
/**
* 创建玩家
* @param array $data
* @return array|mixed|Response
* @throws GameException|Exception
*/
public function createPlayer(array $data = [])
{
$params = [
'random' => $data['uuid'],
'sn' => $this->sn,
'secretCode' => $this->secretCode,
];
$params['digest'] = $this->createSign($params);
$params['loginId'] = $data['uuid'];
$params['nickname'] = $data['name'];
$params['agentLoginId'] = $this->adminUser;
unset($params['secretCode']);
$postData = $this->buildParams($params, 'open.user.create');
$result = doCurl($this->apiDomain,$postData);
if (!empty($result['result']) && empty($result['error'])){
$playerGamePlatform = new PlayerGamePlatform();
$playerGamePlatform->player_id = $this->player->id;
$playerGamePlatform->platform_id = $this->platform->id;
$playerGamePlatform->player_name = $data['name'];
$playerGamePlatform->player_code = $result['result']['loginId'];
$playerGamePlatform->save();
$this->loginId = $playerGamePlatform->player_code;
return $result;
}else{
throw new GameException($result['error']['message'], 0);
}
}
/**
* 获取玩家信息
* @return array|mixed|Response
* @throws GameException|Exception
*/
public function getPlayer()
{
$params = [
'random' => $this->player->uuid ?? uniqid(),
'sn' => $this->sn,
'loginId' => $this->loginId,
'secretCode' => $this->secretCode,
];
$params['digest'] = $this->createSign($params);
unset($params['secretCode']);
$postData = $this->buildParams($params, 'open.user.get');
$result = doCurl($this->apiDomain,$postData);
if (!empty($result['result']) && empty($result['error'])){
return $result;
}else{
throw new GameException($result['error']['message'], 0);
}
}
/**
* 玩家进入游戏
* @param array $data
* @return string
* @throws GameException|\think\Exception
*/
public function login(array $data = []): string
{
$params = [
'random' => $this->player->uuid ?? uniqid(),
'sn' => $this->sn,
'loginId' => $this->loginId,
'secretCode' => $this->secretCode,
];
$params['digest'] = $this->createSign($params);
$params['isMobileUrl'] = 1;
unset($params['secretCode']);
$postData = $this->buildParams($params, 'open.video.game.url');
$result = doCurl($this->apiDomain,$postData);
if (!empty($result['error'])) {
throw new GameException($result['error']['message'], 0);
}
return $result['result'];
}
/**
* 获取玩家游戏平台余额
* @throws GameException|\think\Exception
*/
public function getBalance()
{
$params = [
'random' => $this->player->uuid ?? uniqid(),
'sn' => $this->sn,
'loginId' => $this->loginId,
'secretCode' => $this->secretCode,
];
$params['digest'] = $this->createSign($params);
unset($params['secretCode']);
$postData = $this->buildParams($params, 'open.balance.get');
$result = doCurl($this->apiDomain,$postData);
if (empty($result['error'])){
return $result['result'];
}else{
throw new GameException('Big Gaming System Error,Please contact the administrator', 0);
}
}
/**
* 玩家钱包转入游戏平台
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function balanceTransferOut()
{
return $this->setBalanceTransfer(PlayerWalletTransfer::TYPE_OUT, $this->player->wallet->money);
}
/**
* 游戏平台转入玩家钱包
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function balanceTransferIn()
{
$balance = $this->getBalance();
return $this->setBalanceTransfer(PlayerWalletTransfer::TYPE_IN, -$balance);
}
/**
* 轉帳進出額度
* @param $type
* @param float $amount
* @param float $reward
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
protected function setBalanceTransfer($type, float $amount = 0, float $reward = 0)
{
if ($amount == 0) {
// 记录玩家钱包转出转入记录
$this->createWalletTransfer($type, $amount, $reward);
return true;
}
$params = [
'random' => $this->player->uuid ?? uniqid(),
'sn' => $this->sn,
'loginId' => $this->loginId,
'amount' => $amount,
'secretCode' => $this->secretCode,
];
$params['digest'] = $this->createSign($params);
unset($params['secretCode']);
$postData = $this->buildParams($params, 'open.balance.transfer');
$result = doCurl($this->apiDomain,$postData);
if (!empty($result['error'])) {
throw new GameException($result['error']['message'], 0);
}
// 记录玩家钱包转出转入记录
$this->createWalletTransfer($type, $amount, $reward);
return $result;
}
/**
* 查询额度转移纪录
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getTransferList(string $startTime = '', string $endTime = '', int $page = 1, int $pageSize = 15)
{
$params = [
'random' => $this->player->uuid ?? uniqid(),
'sn' => $this->sn,
'secretKey' => $this->appSecret,
];
$params['sign'] = $this->createSign($params);
$params['loginId'] = $this->loginId;
$params['startTime'] = $startTime;
$params['endTime'] = $endTime;
$params['timeZone'] = 1;
unset($params['secretKey']);
$postData = $this->buildParams($params, 'open.balance.transfer.query');
$result = doCurl($this->apiDomain,$postData);
if (!empty($result['error'])) {
throw new GameException($result['error']['message'], 0);
}
return $result;
}
/**
* 查询注单统计结果
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getOrderSum(string $startTime = '', string $endTime = '', int $page = 1, int $pageSize = 500)
{
$params = [
'random' => $this->player->uuid ?? uniqid(),
'sn' => $this->sn,
'secretKey' => $this->appSecret,
];
$params['sign'] = $this->createSign($params);
$params['loginIds'] = [$this->loginId];
$params['startTime'] = $startTime;
$params['endTime'] = $endTime;
unset($params['secretKey']);
$postData = $this->buildParams($params, 'open.sn.order.sum');
$result = doCurl($this->apiDomain,$postData);
if (!empty($result['error'])) {
throw new GameException($result['error']['message'], 0);
}
return $result;
}
/**
* 按玩家统计注单
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getUserOrderSum(string $startTime = '', string $endTime = '')
{
$params = [
'random' => $this->player->uuid ?? uniqid(),
'sn' => $this->sn,
'secretKey' => $this->appSecret,
];
$params['sign'] = $this->createSign($params);
$params['startTime'] = $startTime;
$params['endTime'] = $endTime;
unset($params['secretKey']);
$postData = $this->buildParams($params, 'open.user.order.sum');
$result = doCurl($this->apiDomain,$postData);
if (!empty($result['error'])) {
throw new GameException($result['error']['message'], 0);
}
return $result['result'];
}
/**
* 查询玩家游戏记录
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getUserGameRecord(string $startTime = '', string $endTime = '')
{
$params = [
'random' => $this->player->uuid ?? uniqid(),
'sn' => $this->sn,
'loginId' => $this->loginId,
'secretKey' => $this->appSecret,
];
$params['sign'] = $this->createSign($params);
$params['startTime'] = $startTime;
$params['endTime'] = $endTime;
unset($params['secretKey']);
$postData = $this->buildParams($params, 'open.video.round.query');
$result = doCurl($this->apiDomain,$postData);
if (!empty($result['error'])) {
throw new GameException($result['error']['message'], 0);
}
return $result['result'];
}
/**
* 查询玩家游戏记录
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function handleOrderHistories()
{
try {
$page = 1;
$list = [];
$timezone = new DateTimeZone('America/New_York');
$start = new DateTime('-6 minutes', $timezone);
$end = new DateTime('-5 minutes', $timezone);
$startTime = $start->format('Y-m-d H:i:s');
$endTime = $end->format('Y-m-d H:i:s');
$data = $this->getAgentOrderRecord($startTime, $endTime, $page);
$fishData = $this->getUserFishOrderRecord($startTime, $endTime, $page);
if (!empty($data['result'])) {
$total = $data['result']['total'] ?? 0;
if ($total > 0) {
$pageSize = 200;
if (!empty($data['result']['items'])) {
foreach ($data['result']['items'] as $item) {
$list[] = [
'uuid' => $item['loginId'],
'platform_id' => $this->platform->id,
'game_code' => $item['playNameEn'],
'bet' => abs($item['bAmount']),
'win' => $item['aAmount'],
'order_no' => $item['orderId'],
'original_data' => json_encode($item,JSON_UNESCAPED_UNICODE),
'platform_action_at' => $item['lastUpdateTime'],
'game_type' => 5,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
];
}
}
if ($total > $pageSize) {
$totalPages = ceil($total / $pageSize);
for ($page = 2; $page <= $totalPages; $page++) {
$nextData = $this->getAgentOrderRecord($startTime,$endTime,$page);
if (!empty($nextData['result']['items'])) {
foreach ($nextData['result']['items'] as $item) {
$list[] = [
'uuid' => $item['loginId'],
'platform_id' => $this->platform->id,
'game_code' => $item['playNameEn'],
'bet' => abs($item['bAmount']),
'win' => $item['aAmount'],
'order_no' => $item['orderId'],
'original_data' => json_encode($item,JSON_UNESCAPED_UNICODE),
'platform_action_at' => $item['lastUpdateTime'],
'game_type' => 5,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
];
}
}
}
}
}
}
if (!empty($fishData['result'])) {
$fishTotal = $fishData['result']['total'] ?? 0;
if ($fishTotal > 0) {
$pageSize = 200;
if (!empty($fishData['result']['items'])) {
foreach ($fishData['result']['items'] as $item) {
$list[] = [
'uuid' => $item['loginId'],
'platform_id' => $this->platform->id,
'game_code' => $item['gameType'],
'bet' => abs($item['betAmount']),
'win' => $item['calcAmount'],
'order_no' => $item['betId'],
'original_data' => json_encode($item, JSON_UNESCAPED_UNICODE),
'platform_action_at' => $item['orderTimeBj'],
'game_type' => 4,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
];
}
}
if ($fishTotal > $pageSize) {
$totalPages = ceil($fishTotal / $pageSize);
for ($page = 2; $page <= $totalPages; $page++) {
$nextFishData = $this->getUserFishOrderRecord($startTime,$endTime,$page);
if (!empty($nextFishData['result']['items'])) {
foreach ($nextFishData['result']['items'] as $item) {
$list[] = [
'uuid' => $item['loginId'],
'platform_id' => $this->platform->id,
'game_code' => $item['gameType'],
'bet' => abs($item['betAmount']),
'win' => $item['calcAmount'],
'order_no' => $item['betId'],
'original_data' => json_encode($item, JSON_UNESCAPED_UNICODE),
'platform_action_at' => $item['orderTimeBj'],
'game_type' => 4,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
];
}
}
}
}
}
}
} catch (Exception $e) {
return [];
}
return $list;
}
/**
* 查询玩家注单
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getUserOrderRecord(string $startTime = '', string $endTime = '', $page = 1)
{
$pageSize = 200;
$params = [
'random' => $this->player->uuid ?? uniqid(),
'sn' => $this->sn,
'secretKey' => $this->appSecret,
];
$params['sign'] = $this->createSign($params);
$params['startTime'] = $startTime;
$params['endTime'] = $endTime;
$params['pageIndex'] = $page;
$params['pageSize'] = $pageSize;
unset($params['secretKey']);
$postData = $this->buildParams($params, 'open.order.query');
$result = doCurl($this->apiDomain,$postData);
if (!empty($result['error'])) {
throw new GameException($result['error']['message'], 0);
}
return $result;
}
/**
* 查询玩家注单详情
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getUserOrderDetail($orderId)
{
$params = [
'random' => $this->player->uuid ?? uniqid(),
'sn' => $this->sn,
'orderId' => $orderId,
'reqTime' => date('Y-m-d H:i:s'),
'secretKey' => $this->appSecret,
];
$params['sign'] = $this->createSign($params);
unset($params['secretKey']);
$postData = $this->buildParams($params, 'open.sn.video.order.detail');
$result = doCurl($this->apiDomain,$postData);
if (!empty($result['error'])) {
throw new GameException($result['error']['message'], 0);
}
return $result['result'];
}
/**
* 查询玩家注单详情地址
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getUserOrderDetailUrl($orderId)
{
$params = [
'random' => $this->player->uuid ?? uniqid(),
'sn' => $this->sn,
'orderId' => $orderId,
'secretKey' => $this->appSecret,
];
$params['sign'] = $this->createSign($params);
unset($params['secretKey']);
$postData = $this->buildParams($params, 'open.sn.video.order.detail.url.get');
$result = doCurl($this->apiDomain,$postData);
if (!empty($result['error'])) {
throw new GameException($result['error']['message'], 0);
}
return $result['result'];
}
/**
* 查询玩家捕鱼注单
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getUserFishOrderRecord(string $startTime = '', string $endTime = '', $page = 1)
{
$pageSize = 200;
$params = [
'random' => $this->player->uuid ?? uniqid(),
'sn' => $this->sn,
'secretKey' => $this->appSecret,
];
$params['sign'] = $this->createSign($params);
$params['gameType'] = 1;
$params['agentLoginId'] = $this->adminUser;
$params['startTime'] = $startTime;
$params['endTime'] = $endTime;
$params['pageIndex'] = $page;
$params['pageSize'] = $pageSize;
unset($params['secretKey']);
$postData = $this->buildParams($params, 'open.order.bg.query');
$result = doCurl($this->apiDomain,$postData);
if (!empty($result['error'])) {
throw new GameException($result['error']['message'], 0);
}
return $result;
}
/**
* 查询代理注单
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getAgentOrderRecord(string $startTime = '', string $endTime = '', $page = 1)
{
$pageSize = 200;
$params = [
'random' => $this->player->uuid ?? uniqid(),
'sn' => $this->sn,
'secretCode' => $this->secretCode,
];
$params['digest'] = $this->createSign($params);
$params['agentLoginId'] = $this->adminUser;
$params['startTime'] = $startTime;
$params['endTime'] = $endTime;
$params['pageIndex'] = $page;
$params['pageSize'] = $pageSize;
unset($params['secretCode']);
$postData = $this->buildParams($params, 'open.order.agent.query');
$result = doCurl($this->apiDomain,$postData);
if (!empty($result['error'])) {
throw new GameException($result['error']['message'], 0);
}
return $result;
}
/**
* 查询投注限红盘口
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getLimitations()
{
$params = [
'random' => $this->player->uuid ?? uniqid(),
'sn' => $this->sn,
'time' => date('Y-m-d H:i:s'),
'secretKey' => $this->appSecret,
];
$params['sign'] = $this->createSign($params);
unset($params['secretKey']);
$postData = $this->buildParams($params, 'open.game.limitations.get');
$result = doCurl($this->apiDomain,$postData);
if (!empty($result['error'])) {
throw new GameException($result['error']['message'], 0);
}
return $result;
}
/**
* 修改投注限红盘口
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function setLimitations($value)
{
$params = [
'random' => $this->player->uuid ?? uniqid(),
'sn' => $this->sn,
'time' => date('Y-m-d H:i:s'),
'secretKey' => $this->appSecret,
];
$params['sign'] = $this->createSign($params);
$params['loginId'] = $this->loginId;
$params['value'] = $value;
unset($params['secretKey']);
$postData = $this->buildParams($params, 'open.game.limitations.set');
$result = doCurl($this->apiDomain,$postData);
if (!empty($result['error'])) {
throw new GameException($result['error']['message'], 0);
}
return $result;
}
/**
* 获取限红盘口列表
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getLimitationsList()
{
$params = [
'random' => $this->player->uuid ?? uniqid(),
'sn' => $this->sn,
'time' => date('Y-m-d H:i:s'),
'secretKey' => $this->appSecret,
];
$params['sign'] = $this->createSign($params);
unset($params['secretKey']);
$postData = $this->buildParams($params, 'open.game.limitations.list');
$result = doCurl($this->apiDomain,$postData);
if (!empty($result['error'])) {
throw new GameException($result['error']['message'], 0);
}
return $result;
}
}

View File

@@ -0,0 +1,371 @@
<?php
namespace app\service\game;
use addons\webman\model\Game;
use addons\webman\model\GamePlatform;
use addons\webman\model\Player;
use addons\webman\model\PlayerGamePlatform;
use addons\webman\model\PlayerWalletTransfer;
use app\exception\GameException;
use Exception;
use support\Log;
use support\Response;
class CSServiceInterface extends GameServiceFactory implements GameServiceInterface
{
public $method = 'POST';
public $successCode = '0';
/** @var PlayerGamePlatform $playerGamePlatform */
public $playerGamePlatform;
public $gameType = [
'CB' => 'CARD & BOARDGAME',
'ES' => 'E-GAMES',
'SB' => 'SPORTBOOK',
'LC' => 'LIVE-CASINO',
'SL' => 'SLOTS',
'LK' => 'LOTTO',
'FH' => 'FISH HUNTER',
'PK' => 'POKER',
'MG' => 'MINI GAME',
'OT' => 'OTHERS'
];
public $failCode = [
'61' => '货币不兼容',
'70' => '集成系统余额不足',
'71' => '单据号不正确',
'72' => '余额不足',
'73' => '转账金额不正确',
'74' => '转账金额不能多过两个小数点 0.00',
'75' => '不允许在游戏中进行转移',
'81' => '会员账号不存在',
'82' => '会员账号已存在',
'83' => '代理号已存在',
'90' => '请求参数不正确',
'91' => '代理号不正确',
'92' => '供应商代号不正确',
'93' => '请求参数类型不正确',
'94' => '账号不正确',
'95' => '密码不正确',
'96' => '旧密码不正确',
'97' => '请求链接/域名不正确',
'98' => '账号/密码错误',
'99' => '加密错误',
'600' => '前期检验失败。 存款/取款 操作已被无视',
'601' => '此产品的存款 功能暂时停用维修',
'602' => '此产品的取款 功能暂时停用维修',
'603' => '即将执行在线系统维护为了避免维护时导致的系统不稳定转账API暂时停止暂停时间大约510分钟若提早完毕会提早解放',
'992' => '平台不兼容请求的游戏类型',
'991' => '代理号已冻结',
'994' => '接口访问被禁止',
'995' => '平台未开通',
'996' => '平台不支持',
'998' => '请联系客服',
'999' => '系统维护中',
'9999' => '未知错误',
'-987' => '交易单号不存在;产品不支持',
'-997' => '系统错误,请联络客服。',
'-998' => '集成系统接口余额不足',
'-999' => '接口错误',
];
private $apiDomain;
private $providerCode;
private $appId;
private $appSecret;
private $path = [
'createPlayer' => '/createMember.aspx',
'getGameList' => '/getGameList.aspx',
'getBalance' => '/getBalance.aspx',
'getLoginH5' => '/launchGames.aspx',
'getDLoginH5' => '/launchDGames.ashx',
'changePassword' => '/changePassword.aspx',
'checkAgentCredit' => '/checkAgentCredit.aspx',
'checkMemberProductUsername' => '/checkMemberProductUsername.aspx',
'launchAPP' => '/launchAPP.ashx',
'checkTransaction' => '/checkTransaction.ashx',
'setBalanceTransfer' => '/makeTransfer.aspx',
'getDailyWager' => '/getDailyWager.ashx',
'fetchArchieve' => '/fetchArchieve.aspx',
'markbyjson' => '/markbyjson.aspx',
'markArchieve' => '/markArchieve.ashx',
'getGameRecord' => '/fetchbykey.aspx',
];
private $lang = [
'zh-CN' => 'zh-ch',
'en' => 'en_us',
'zh_tc' => 'zh_tc',
'en-us' => 'en-us',
'id' => 'id',
'th' => 'th',
'my' => 'my',
'vi' => 'vi',
'fi_fi' => 'fi_fi',
'kr_ko' => 'kr_ko',
'hi_hi' => 'hi_hi',
'br_po' => 'br_po',
'lo_la' => 'lo_la',
'cam_dia' => 'cam_dia', // 柬埔寨语
];
/**
* @param Player|null $player
* @param $type
* @throws Exception
*/
public function __construct($type, Player $player = null)
{
$config = config('game_platform.' . $type);
$this->appId = $config['app_id'];
$this->appSecret = $config['app_secret'];
$this->apiDomain = $config['api_domain'];
$this->providerCode = $config['provider_code'];
$this->platform = GamePlatform::query()->where('name', $type)->first();
if (!empty($player)) {
$this->player = $player;
/** @var PlayerGamePlatform $playerGamePlatform */
$playerGamePlatform = $this->player->playerGamePlatform->where('platform_id', $this->platform->id)->first();
if (empty($playerGamePlatform)) {
$this->createPlayer();
} else {
$this->playerGamePlatform = $playerGamePlatform;
}
}
}
/**
* 创建玩家 MD5(operatorcode + username +secret_key)
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function createPlayer()
{
$userName = mb_strtolower($this->providerCode . generateUniqueUsername());
$params = [
'operatorcode' => $this->appId,
'username' => $userName,
];
$params['signature'] = mb_strtoupper(md5($this->appId . $userName . $this->appSecret));
$res = dogGetCurl($this->createUrl('createPlayer'), $params);
if ($res['errCode'] != $this->successCode) {
throw new GameException($this->failCode[$res['errCode']], 0);
}
$playerGamePlatform = new PlayerGamePlatform();
$playerGamePlatform->player_id = $this->player->id;
$playerGamePlatform->platform_id = $this->platform->id;
$playerGamePlatform->player_name = $this->player->name;
$playerGamePlatform->player_code = $userName;
$playerGamePlatform->player_password = generateRandomPassword();
$playerGamePlatform->save();
$this->playerGamePlatform = $playerGamePlatform;
return $res;
}
/**
* 生成请求url
* @param $method
* @return string
*/
public function createUrl($method): string
{
return $this->apiDomain . $this->path[$method];
}
/**
* 查询玩家游戏平台帐号接口 MD5(operatorcode + providercode + username + secret_key)
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getPlayer()
{
$params = [
'operatorcode' => $this->appId,
'providercode' => $this->providerCode,
'username' => $this->playerGamePlatform->player_code,
];
$params['signature'] = mb_strtoupper(md5($this->appId . $this->providerCode . $this->playerGamePlatform->player_code . $this->appSecret));
$res = dogGetCurl($this->createUrl('checkMemberProductUsername'), $params);
if ($res['errCode'] != $this->successCode) {
throw new GameException($this->failCode[$res['errCode']], 0);
}
return $res;
}
/**
* 获取游戏列表 MD5(operatorcode.toLower() + providercode.toUpper() + secret_key)
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getSimpleGameList()
{
$params = [
'providercode' => $this->providerCode,
'operatorcode' => $this->appId,
'lang' => 'en',
'html' => '0',
'reformatJson' => 'yes',
];
$params['signature'] = strtoupper(md5(mb_strtolower($this->appId) . mb_strtoupper($this->providerCode) . $this->appSecret));
$data = dogGetCurl($this->createUrl('getGameList'), $params);
if ($data['errCode'] != $this->successCode) {
throw new GameException($this->failCode[$data['errCode']], 0);
}
if (!empty($data['gamelist'])) {
$gameList = json_decode($data['gamelist'], true);
foreach ($gameList as $game) {
try {
Game::query()->updateOrCreate(
[
'platform_id' => $this->platform->id,
'game_code' => $game['g_code']
],
[
'platform_game_type' => $game['p_type'],
'game_data' => json_encode($game),
'name' => $game['gameName']['gameName_enus'] ?? '',
'game_image' => $game['imgFileName'] ?? '',
]
);
} catch (Exception $e) {
Log::error($e->getMessage());
}
}
}
return $data;
}
/**
* 登录游戏MD5(operatorcode + password + providercode + type + username + secret_key)
* @param array $data
* @return string
* @throws GameException|\think\Exception
*/
public function login(array $data = []): string
{
$params = [
'operatorcode' => $this->appId,
'providercode' => $this->providerCode,
'username' => $this->playerGamePlatform->player_code,
'password' => $this->playerGamePlatform->player_password,
'type' => $data['platformGameType'],
'gameid' => $data['gameCode'] ?? 0,
'lang' => 'en',
'html5' => 1,
];
$params['signature'] = mb_strtoupper(md5($this->appId . $this->playerGamePlatform->player_password . $this->providerCode . $data['platformGameType'] . $this->playerGamePlatform->player_code . $this->appSecret));
$res = dogGetCurl($this->createUrl('getLoginH5'), $params);
if ($res['errCode'] != $this->successCode) {
Log::error($this->failCode[$res['errCode']], ['res' => $res]);
throw new GameException($this->failCode[$res['errCode']], 0);
}
return $res['gameUrl'];
}
/**
* 玩家钱包转入游戏平台
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function balanceTransferOut()
{
return $this->setBalanceTransfer(PlayerWalletTransfer::TYPE_OUT, intval($this->player->wallet->money));
}
/**
* 资金转账接口MD5(amount + operatorcode + password + providercode + referenceid + type + username + secret_key)
* @param $type
* @param float $amount
* @return array|mixed|null
* @throws GameException
* @throws \think\Exception
*/
protected function setBalanceTransfer($type, float $amount = 0)
{
if ($type == PlayerWalletTransfer::TYPE_OUT) {
$platformType = 0;
} else {
$platformType = 1;
}
$no = createOrderNo();
$params = [
'operatorcode' => $this->appId,
'providercode' => $this->providerCode,
'username' => $this->playerGamePlatform->player_code,
'password' => $this->playerGamePlatform->player_password,
'referenceid' => $no,
'type' => $platformType,
'amount' => $amount
];
$params['signature'] = mb_strtoupper(md5($amount . $this->appId . $this->playerGamePlatform->player_password . $this->providerCode . $no . $platformType . $this->playerGamePlatform->player_code . $this->appSecret));
$res = dogGetCurl($this->createUrl('setBalanceTransfer'), $params);
if ($res['errCode'] != $this->successCode) {
throw new GameException($this->failCode[$res['errCode']], 0);
}
// 记录玩家钱包转出转入记录
$this->createWalletTransfer($type, $amount, 0, $res['innerCode'] ?? '');
return $res;
}
/**
* 玩家钱包转入游戏平台
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function balanceTransferIn()
{
$balance = $this->getBalance();
return $this->setBalanceTransfer(PlayerWalletTransfer::TYPE_IN, $balance['balance'] ?? 0);
}
/**
* 獲取玩家餘額信息
* 簽名密鑰方式 MD5(operatorcode + password + providercode + username + secret_key)
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getBalance()
{
$params = [
'operatorcode' => $this->appId,
'providercode' => $this->providerCode,
'username' => $this->playerGamePlatform->player_code,
'password' => $this->playerGamePlatform->player_password
];
$params['signature'] = mb_strtoupper(md5($this->appId . $this->playerGamePlatform->player_password . $this->providerCode . $this->playerGamePlatform->player_code . $this->appSecret));
$res = dogGetCurl($this->createUrl('getBalance'), $params);
if ($res['errCode'] != $this->successCode) {
throw new GameException($this->failCode[$res['errCode']], 0);
}
return $res;
}
/**
* 依據時間獲取遊戲紀錄
* MD5 (Id+Method+SN+StartTime+EndTime+APISecretKey)
* @param int $pageIndex
* @param string $startTime
* @param string $endTime
* @return array|mixed|null
* @throws GameException
*/
public function getGameRecordByTime(int $pageIndex = 1, string $startTime = '', string $endTime = '')
{
}
/**
* 獲取玩家遊戲歷史紀錄
* MD5 (Id+Method+SN+APISecretKey)
* @return array|mixed|null
* @throws GameException
*/
public function getGameRecord()
{
}
}

View File

@@ -0,0 +1,141 @@
<?php
namespace app\service\game;
use addons\webman\model\GamePlatform;
use addons\webman\model\Player;
use addons\webman\model\PlayerDeliveryRecord;
use addons\webman\model\PlayerWalletTransfer;
use Exception;
/**
* 游戏服务工厂
*/
class GameServiceFactory
{
const TYPE_LUCKY365 = 'LUCKY365'; // lucky365
const TYPE_BIGGAMING = 'BIGGAMING'; // BigGaming
const TYPE_JILI = 'JILI'; // jili
const TYPE_MEGA888 = 'MEGA888'; // Mega888
const TYPE_KISS918 = 'KISS918'; // Kiss918
const TYPE_JDB = 'JDB'; // JDB
const TYPE_PRAGMATIC = 'PRAGMATIC'; // PRAGMATIC
const TYPE_MARIOCLUB = 'MARIOCLUB'; // MARIOCLUB
const TYPE_JOKER = 'JOKER'; // JOKER
const TYPE_LIONKING = 'LIONKING'; // LionKing
const TYPE_MONKEY_KING = 'MONKEYKING'; // monkey king
const TYPE_ASIAGAMING = 'ASIAGAMING'; // AsiaGaming
const TYPE_TFGAMING = 'TFGAMING'; // TfGaming
const TYPE_IBC = 'IBC'; // IBC
const TYPE_PLAYSTAR = 'PLAYSTAR'; // PlayStar
const TYPE_AWC68 = 'AWC68'; // AWC68
const TYPE_GAMEPLAY = 'GAMEPLAY'; // GamePlay
const TYPE_NEXTSPIN = 'NEXTSPIN'; // GamePlay
const TYPE_WMCASINO = 'WMCASINO'; // WMcasino
const TYPE_SPADEGAMING = 'SPADEGAMING'; // SpadeGaming
const TYPE_FUNKYGAME = 'FUNKYGAME'; // FunkyGame
const TYPE_R5 = 'R5'; // R5
const TYPE_JK = 'JK'; // JK
const DEVICE_TYPE_WEB = 1; // web
const DEVICE_TYPE_IOS = 2; // ios
const DEVICE_TYPE_ANDROID = 3; // android
/** @var Player $player */
public $player;
/** @var GamePlatform $platform */
public $platform;
/**
* 创建服务
* @throws Exception
*/
public static function createService(string $type, $player = null): GameServiceInterface
{
switch ($type) {
case self::TYPE_LUCKY365:
return new Lucky365ServiceInterface(self::TYPE_LUCKY365, $player);
case self::TYPE_JILI:
return new JiLiServiceInterface(self::TYPE_JILI, $player);
case self::TYPE_MEGA888:
return new MeGa888ServiceInterface(self::TYPE_MEGA888, $player);
case self::TYPE_BIGGAMING:
return new BigGamingServiceInterface(self::TYPE_BIGGAMING, $player);
case self::TYPE_KISS918:
return new Kiss918ServiceInterface(self::TYPE_KISS918, $player);
case self::TYPE_JDB:
return new JDBServiceInterface(self::TYPE_JDB, $player);
case self::TYPE_PRAGMATIC:
return new PragmaticServiceInterface(self::TYPE_PRAGMATIC, $player);
case self::TYPE_MARIOCLUB:
return new MarioClubServiceInterface(self::TYPE_MARIOCLUB, $player);
case self::TYPE_JOKER:
return new JokerServiceInterface(self::TYPE_JOKER, $player);
case self::TYPE_NEXTSPIN:
return new NextSpinServiceInterface(self::TYPE_NEXTSPIN, $player);
case self::TYPE_MONKEY_KING:
return new MonkeyKingServiceInterface(self::TYPE_MONKEY_KING, $player);
case self::TYPE_LIONKING:
return new LionKingServiceInterface(self::TYPE_LIONKING, $player);
case self::TYPE_ASIAGAMING:
case self::TYPE_TFGAMING:
case self::TYPE_IBC:
case self::TYPE_PLAYSTAR:
case self::TYPE_AWC68:
case self::TYPE_GAMEPLAY:
case self::TYPE_WMCASINO:
case self::TYPE_SPADEGAMING:
case self::TYPE_FUNKYGAME:
case self::TYPE_R5:
case self::TYPE_JK:
return new CSServiceInterface($type, $player);
default:
throw new Exception("未知的游戏服务类型: $type");
}
}
/**
* 报错平台转出/入记录
* @param int $type
* @param float $amount
* @param float $reward
* @param string $platformNo
* @return void
*/
public function createWalletTransfer(int $type = 1, float $amount = 0, float $reward = 0, string $platformNo = '')
{
$playerWalletTransfer = new PlayerWalletTransfer();
$playerWalletTransfer->player_id = $this->player->id;
$playerWalletTransfer->platform_id = $this->platform->id;
$playerWalletTransfer->department_id = $this->player->department_id;
$playerWalletTransfer->type = $type;
$playerWalletTransfer->amount = abs($amount);
$playerWalletTransfer->reward = abs($reward);
$playerWalletTransfer->platform_no = $platformNo;
$playerWalletTransfer->tradeno = createOrderNo();
$playerWalletTransfer->save();
$beforeGameAmount = $this->player->wallet->money;
$playerDeliveryRecord = new PlayerDeliveryRecord;
if ($type == PlayerWalletTransfer::TYPE_OUT) {
$this->player->wallet->money = 0;
$playerDeliveryRecord->type = PlayerDeliveryRecord::TYPE_GAME_OUT;
}
if ($type == PlayerWalletTransfer::TYPE_IN) {
$this->player->wallet->money = bcadd($this->player->wallet->money, bcadd(abs($amount), abs($reward), 2), 2);
$playerDeliveryRecord->type = PlayerDeliveryRecord::TYPE_GAME_IN;
}
$this->player->push();
//寫入金流明細
$playerDeliveryRecord->player_id = $playerWalletTransfer->player_id;
$playerDeliveryRecord->department_id = $playerWalletTransfer->department_id;
$playerDeliveryRecord->target = $playerWalletTransfer->getTable();
$playerDeliveryRecord->target_id = $playerWalletTransfer->id;
$playerDeliveryRecord->source = 'play_game';
$playerDeliveryRecord->amount = $playerWalletTransfer->amount;
$playerDeliveryRecord->amount_before = $beforeGameAmount;
$playerDeliveryRecord->amount_after = $this->player->wallet->money;
$playerDeliveryRecord->tradeno = '';
$playerDeliveryRecord->remark = '';
$playerDeliveryRecord->save();
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace app\service\game;
interface GameServiceInterface
{
/**
* 创建玩家
* @return mixed
*/
public function createPlayer();
/**
* 获取玩家信息
* @return mixed
*/
public function getPlayer();
/**
* 获取游戏列表
* @return mixed
*/
public function getSimpleGameList();
/**
* 登录
* @return mixed
*/
public function login();
}

View File

@@ -0,0 +1,511 @@
<?php
namespace app\service\game;
use addons\webman\model\Game;
use addons\webman\model\GamePlatform;
use addons\webman\model\Player;
use addons\webman\model\PlayerGamePlatform;
use addons\webman\model\PlayerWalletTransfer;
use app\exception\GameException;
use Exception;
use support\Response;
class JDBServiceInterface extends GameServiceFactory implements GameServiceInterface
{
public $method = 'POST';
public $successCode = '0000';
public $loginId;
public $gameType = [
'10' => 'Slot',
'12' => 'Casino',
'13' => 'Arcade',
'16' => 'Fishing'
];
public $failCode = [
'0000' => '成功',
'9999' => '失敗',
'9001' => '未授權訪問',
'9002' => '域名為空或域名長度小於 2',
'9003' => '域名驗證失敗。',
'9004' => '加密數據為空或加密數據的長度等於 0。',
'9005' => '斷言SAML未通過時間戳驗證。',
'9006' => '從加密數據中提取 SAML 參數失敗。',
'9007' => '未知操作。',
'9008' => '與之前的值相同。',
'9009' => '超時。',
'9010' => '讀取超時。',
'9011' => '重複交易。',
'9012' => '請稍後再試。',
'9013' => '系統正在維護。',
'9014' => '檢測到多帳戶登錄。',
'9015' => '數據不存在。',
'9016' => '無效令牌。',
'9019' => '請求速率限制超過。',
'9020' => '每次登錄只能獲得一次遊戲票。',
'9021' => '違反一次性會話策略。',
'9022' => '遊戲正在維護。',
'9023' => '不支持的貨幣。',
'9024' => '贏取倍數必須大於或等於 10 倍。',
'9025' => '不支持重放遊戲。',
'9026' => '获胜金额应大于0。',
'9027' => '不支持演示。',
'8000' => '輸入參數錯誤,請檢查您的參數是否正確。',
'8001' => '參數不能為空。',
'8002' => '參數必須是正整數。',
'8003' => '參數不能為負數。',
'8005' => '日期秒格式錯誤',
'8006' => '時間不符合。',
'8007' => '參數只能使用數字。',
'8008' => '找不到參數。',
'8009' => '時間間隔超過允許範圍。',
'8010' => '參數長度太長。',
'8013' => '日期分鐘格式參數錯誤。',
'8014' => '參數不得超過指定的小數位。',
'7001' => '找不到指定的父 ID。',
'7002' => '父級已暫停。',
'7003' => '父級已鎖定。',
'7004' => '父級已關閉。',
'7405' => '您已登出!',
'7501' => '找不到用戶 ID。',
'7502' => '用戶已暫停。',
'7503' => '用戶已鎖定。',
'7504' => '用戶已關閉。',
'7505' => '用戶未在玩遊戲。',
'7506' => '演示帳戶已滿。',
'7601' => '無效的用戶 ID。請僅使用 a-z、0-9 之間的字符。',
'7602' => '帳戶已存在。請選擇其他用戶 ID。',
'7603' => '無效的用戶名。',
'7604' => '密碼必須至少 6 個字符,包含 1 個字母和 1 個數字。',
'7605' => '無效的操作代碼。請僅使用數字 2、3、4、5。',
'6001' => '您的現金餘額不足以取款。',
'6002' => '用戶餘額為零。',
'6003' => '取款金額為負。',
'6004' => '重複轉帳。',
'6005' => '重複的序列號。',
'6009' => '存款金額超過上限。',
'6010' => '餘額超過上限。',
'6011' => '分配的信用額超過上限。',
'6012' => '序列號正在進行中。',
'6901' => '用戶正在玩遊戲,不允許轉移餘額。'
];
private $apiDomain;
private $domain;
private $iv;
private $key;
private $dc;
private $parent;
private $lang = [
'zh-CN' => 'cn',
'zh-TW' => 'cn',
'jp' => 'jpn',
'en' => 'en',
'th' => 'th',
'vi' => 'vi',
'kr_ko' => 'ko',
'id' => 'id',
];
public $localGameType = [
'0' => '1',//斯洛
'32' => '1',//斯洛
'50' => '1',//斯洛
'55' => '1',//斯洛
'57' => '1',//斯洛
'58' => '1',//斯洛
'66' => '1',//斯洛
'80' => '1',//斯洛
'90' => '1',//斯洛
'130' => '1',//斯洛
'7' => '4',//捕鱼
'31' => '4',//捕鱼
'70' => '4',//捕鱼
'59' => '4',//捕鱼
'67' => '4',//捕鱼
'91' => '4',//捕鱼
'8' => '8',//宾果
'12' => '8',//宾果
'60' => '8',//宾果
'9' => '8',//宾果
'22' => '8',//宾果
'30' => '8',//宾果
'56' => '8',//宾果
'75' => '8',//宾果
'81' => '8',//宾果
'92' => '8',//宾果
'131' => '8',//宾果
'120' => '8',//宾果
'18' => '2',//赌场
'93' => '2',//赌场
'132' => '2',//赌场
'41' => '5',//真人视讯
'101' => '5',//真人视讯
];
/**
* @param Player|null $player
* @param $type
* @throws Exception
*/
public function __construct($type, Player $player = null)
{
$config = config('game_platform.' . $type);
$this->iv = $config['iv'];
$this->apiDomain = $config['api_domain'].'/apiRequest.do';
$this->domain = $config['domain'];
$this->key = $config['key'];
$this->dc = $config['dc'];
$this->parent = $config['admin_user'];
$this->platform = GamePlatform::query()->where('name', $type)->first();
if (!empty($player)) {
$this->player = $player;
$this->getLoginId();
}
}
/**
* 生成请求数据
* @param $source
* @return string
*/
public function padString($source): string
{
$paddingChar = ' ';
$size = 16;
$x = strlen($source) % $size;
$padLength = $size - $x;
$source .= str_repeat($paddingChar, $padLength);
return $source;
}
/**
* 生成请求数据
* @param $params
* @return array
*/
public function buildParams($params): array
{
$data = $this->padString(json_encode($params));
$encryptData = openssl_encrypt($data, 'AES-128-CBC', $this->key, OPENSSL_NO_PADDING, $this->iv);
$reqBase64 = base64_encode($encryptData);
return [
'dc' => $this->dc,
'x' => str_replace(array('+','/','=') , array('-','_','') , $reqBase64)
];
}
/**
* @throws Exception
*/
protected function getLoginId()
{
/** @var PlayerGamePlatform $playerGamePlatform */
$playerGamePlatform = PlayerGamePlatform::query()
->where('platform_id', $this->platform->id)
->where('player_id',$this->player->id)
->first();
if (!empty($playerGamePlatform)) {
return $this->loginId = $playerGamePlatform->player_code;
}
return $this->createPlayer([
'uuid' => $this->player->uuid,
'name' => $this->player->name,
]);
}
/**
* 创建玩家
* @param array $data
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function createPlayer(array $data = [])
{
$params = [
'action' => 12,
'ts' => round(microtime(true) * 1000),
'lang' => 'cn',
'parent' => $this->parent,
'uid' => $this->player->uuid,
'name' => $this->player->name ?: $this->player->uuid
];
$request = $this->buildParams($params);
$res = doFormCurl($this->apiDomain, $request);
if ($res['status'] != $this->successCode) {
throw new GameException($this->failCode[$res['status']], 0);
}
$playerGamePlatform = new PlayerGamePlatform();
$playerGamePlatform->player_id = $this->player->id;
$playerGamePlatform->platform_id = $this->platform->id;
$playerGamePlatform->player_name = $data['name'];
$playerGamePlatform->player_code = $data['uuid'];
$playerGamePlatform->save();
$this->loginId = $playerGamePlatform->player_code;
return $res;
}
public function getPlayer()
{
// TODO: Implement getPlayer() method.
}
/**
* 获取游戏摘要MD5 (id+method+sn+APlSecretKey)
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getSimpleGameList()
{
$params = [
'action' => 49,
'ts' => round(microtime(true) * 1000),
'parent' => $this->parent,
'lang' => 'en',
];
$request = $this->buildParams($params);
$res = doFormCurl($this->apiDomain, $request);
if ($res['status'] != $this->successCode) {
throw new GameException($this->failCode[$res['status']], 0);
}
$insertData = [];
if (!empty($res['data'])) {
foreach ($res['data'] as $data) {
foreach($data['list'] as $item){
$insertData[] = [
'platform_id' => $this->platform->id,
'game_code' => $item['mType'],
'platform_game_type' => $data['gType'],
'game_type' => $this->localGameType[$data['gType']],
'name' => $item['name'],
'game_image' => $item['image'],
];
}
}
}
if (!empty($insertData)) {
Game::query()->upsert($insertData, ['platform_id', 'game_code']);
}
return $insertData;
}
/**
* 获取游戏摘要MD5 (id+method+sn+APlSecretKey)
* @param array $data
* @return string
* @throws GameException|\think\Exception
*/
public function login(array $data = []): string
{
$params = [
'action' => 11,
'ts' => round(microtime(true) * 1000),
'lang' => $this->lang[$data['lang']] ?? 'en',
'uid' => $this->loginId,
'gType' => $data['platformGameType'],
'mType' => $data['gameCode'],
'windowMode' => 2,
'isAPP' => true,
];
// if($data['platformGameType'] || $data['gameCode']){
// $params['gType'] = $data['platformGameType'];
// $params['mType'] = $data['gameCode'];
// $params['windowMode'] = 2;
// $params['isAPP'] = true;
// }
$request = $this->buildParams($params);
$res = doFormCurl($this->apiDomain, $request);
if ($res['status'] != $this->successCode) {
throw new GameException($this->failCode[$res['status']], 0);
}
return $res['path'] ?? '';
}
/**
* 玩家钱包转入游戏平台
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function balanceTransferOut()
{
return $this->setBalanceTransfer(PlayerWalletTransfer::TYPE_OUT, $this->player->wallet->money);
}
/**
* 转入游戏平台玩家钱包
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function balanceTransferIn()
{
//提现之前把用户踢下线
if($this->checkPlay()){
$this->userLogout();
sleep(1);
}
$balance = $this->getBalance();
if($balance == 0){
// 记录玩家钱包转出转入记录
$this->createWalletTransfer(PlayerWalletTransfer::TYPE_IN, 0, 0);
return true;
}
return $this->setBalanceTransfer(PlayerWalletTransfer::TYPE_IN, $balance ? -$balance : 0);
}
/**
* 轉帳進出額度
* @param $type
* @param float $amount
* @param float $reward
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
protected function setBalanceTransfer($type, float $amount = 0, float $reward = 0)
{
$params = [
'action' => 19,
'ts' => round(microtime(true) * 1000),
'parent' => $this->parent,
'uid' => $this->loginId,
'serialNo' => createOrderNo(),
'amount' => $amount ?? 0,
];
$request = $this->buildParams($params);
$res = doFormCurl($this->apiDomain, $request);
if ($res['status'] != $this->successCode) {
throw new GameException($this->failCode[$res['status']], 0);
}
// 记录玩家钱包转出转入记录
$this->createWalletTransfer($type, $amount, $reward);
return $res;
}
/**
* 獲取玩家餘額信息
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getBalance()
{
$params = [
'action' => 15,
'ts' => round(microtime(true) * 1000),
'parent' => $this->parent,
'uid' => $this->loginId,
];
$request = $this->buildParams($params);
$res = doFormCurl($this->apiDomain, $request);
if ($res['status'] != $this->successCode) {
throw new GameException('JDB System Error,Please contact the administrator', 0);
}
return $res['data'][0]['balance'] ?? 0;
}
/**
* 查询玩家状态
* @throws GameException|\think\Exception
*/
public function checkPlay(): bool
{
$params = [
'action' => 52,
'ts' => round(microtime(true) * 1000),
'parent' => $this->parent,
'uid' => $this->loginId,
];
$request = $this->buildParams($params);
$res = doFormCurl($this->apiDomain, $request);
//游戏中
if($res['status'] == $this->successCode){
return true;
}
//不在游戏中
if($res['status'] == 7505){
return false;
}
throw new GameException($this->failCode[$res['status']], 0);
}
/**
* 玩家踢下线
* @throws GameException|\think\Exception
*/
public function userLogout(): bool
{
$params = [
'action' => 17,
'ts' => round(microtime(true) * 1000),
'parent' => $this->parent,
'uid' => $this->loginId,
];
$request = $this->buildParams($params);
$res = doFormCurl($this->apiDomain, $request);
if ($res['status'] != $this->successCode) {
throw new GameException($this->failCode[$res['status']], 0);
}
return true;
}
/**
* 取得區間內遊戲紀錄
* @return array
* @throws Exception
*/
public function handleOrderHistories(): array
{
$list = [];
try {
$data = $this->getGameHistories();
if (!empty($data)) {
foreach ($data as $item) {
if ($item['gType'] == 9 && !empty($item['hasGamble'])) {
$item['bet'] = $item['gambleBet'];
}
$list[] = [
'uuid' => $item['playerId'],
'platform_id' => $this->platform->id,
'game_code' => $item['mtype'],
'bet' => abs($item['bet']),
'win' => max($item['win'], 0),
'order_no' => $item['historyId'],
'game_type' => $item['gType'],
'original_data' => json_encode($item,JSON_UNESCAPED_UNICODE),
'platform_action_at' => date('Y-m-d H:i:s', strtotime($item['lastModifyTime'])),
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
];
}
}
} catch (Exception $e) {
return [];
}
return $list;
}
/**
* 取得區間內遊戲紀錄
* @return array
* @throws GameException|\think\Exception
*/
public function getGameHistories(): array
{
$params = [
'action' => 29,
'ts' => round(microtime(true) * 1000),
'parent' => $this->parent,
'starttime' => date('d-m-Y H:i:00', strtotime('-5 minutes')),
'endtime' => date('d-m-Y H:i:00', strtotime('-4 minutes')),
];
$request = $this->buildParams($params);
$res = doFormCurl($this->apiDomain, $request);
if ($res['status'] != $this->successCode) {
throw new GameException($this->failCode[$res['status']], 0);
}
return $res['data'] ?? [];
}
}

View File

@@ -0,0 +1,529 @@
<?php
namespace app\service\game;
use addons\webman\model\Game;
use addons\webman\model\GamePlatform;
use addons\webman\model\Player;
use addons\webman\model\PlayerGamePlatform;
use addons\webman\model\PlayerWalletTransfer;
use app\exception\GameException;
use DateTime;
use DateTimeZone;
use Exception;
use Illuminate\Support\Str;
use support\Log;
use support\Response;
class JiLiServiceInterface extends GameServiceFactory implements GameServiceInterface
{
public $method = 'POST';
private $apiDomain;
private $domain;
private $appId;
private $appSecret;
public $loginId;
public $gameType = [
'2' => 'Casino',
'5' => 'Fishing',
'8' => 'Bingo',
'1' => 'Slot',
];
public $localGameType = [
'2' => '2',//赌场
'5' => '4',//捕鱼
'8' => '8',//宾果
'1' => '1',//斯洛
];
public $failCode = [
'S200' => '重復請求',
'F0001' => '無效的簽名',
'F0002' => '無效的SN',
'F0003' => '無效的參數',
'F0004' => '無效的貨幣',
'F0005' => '玩家已存在',
'F0006' => '玩家不存在',
'F0007' => '會員不存在',
'F0008' => '執行失敗',
'F0009' => '無效的方法',
'F0010' => '無效的用戶狀態',
'F0011' => '玩家狀態無需更新',
'F0012' => '超出數據範圍',
'F0013' => '無匹配數據',
'F0014' => '登入位置被禁止',
'F0015' => '分數不足夠',
'F0016' => '不支持禮碼',
'F0017' => '交易流水號不得重複',
'F0018' => '系統繁忙',
'F0019' => '日期時間各式錯誤',
'F0020' => '超出時間限制範圍開始時間與結束時間之間不能大於120分鐘',
'F0021' => '執行取消',
'M0001' => '系統維護',
'M0002' => '系統錯誤',
];
/**
* @param Player|null $player
* @param $type
* @throws Exception
*/
public function __construct($type, Player $player = null)
{
$config = config('game_platform.' . $type);
$this->appId = $config['app_id'];
$this->apiDomain = $config['api_domain'];
$this->domain = $config['domain'];
$this->appSecret = $config['app_secret'];
$this->platform = GamePlatform::query()->where('name', $type)->first();
if (!empty($player)) {
$this->player = $player;
$this->getLoginId();
}
}
/**
* 生成请求url
* @param $method
* @return string
*/
public function createUrl($method): string
{
return $this->apiDomain.$method;
}
public function createSign($params): string
{
$date = new DateTime('now', new DateTimeZone('UTC'));
$date->setTimezone(new DateTimeZone('America/Puerto_Rico'));
$key_g = md5($date->format('ymj').$this->appId.$this->appSecret);
$paramStr = urldecode(http_build_query($params));
$key = mt_rand(100000,999999).MD5($paramStr.$key_g).mt_rand(100000,999999);
return $key;
}
/**
* 更新游戏列表
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getSimpleGameList()
{
$params = [
'AgentId' => $this->appId
];
$signature = $this->createSign($params);
$params['Key'] = $signature;
$result = doFormCurl($this->createUrl('GetGameList'), $params);
if ($result['ErrorCode'] == 0 && !empty($result['Data'])) {
foreach ($result['Data'] as $game) {
if($game['GameCategoryId'] == 3){
continue;
}
Game::query()->updateOrCreate(
[
'platform_id' => $this->platform->id,
'game_code' => $game['GameId'],
],
[
'platform_game_type' => $game['GameCategoryId'],
'game_type' => $this->localGameType[$game['GameCategoryId']],
'name' => $game['name']['en-US'],
]
);
}
}else{
throw new GameException($result['Message'], 0);
}
return $result;
}
/**
* 获取游戏列表
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getGamesList()
{
$params = [
'AgentId' => $this->appId
];
$signature = $this->createSign($params);
$params['Key'] = $signature;
$result = doFormCurl($this->createUrl('GetGameList'), $params);
if ($result['ErrorCode'] == 0 && !empty($result['Data'])) {
return $result;
}else{
throw new GameException($result['Message'], 0);
}
}
/**
* 获取玩家ID
* @throws Exception
*/
protected function getLoginId()
{
/** @var PlayerGamePlatform $playerGamePlatform */
$playerGamePlatform = PlayerGamePlatform::query()->where('platform_id', $this->platform->id)->where('player_id',$this->player->id)->first();
if (!empty($playerGamePlatform)) {
return $this->loginId = $playerGamePlatform->player_code;
}
return $this->createPlayer([
'uuid' => $this->player->uuid,
'name' => $this->player->name,
]);
}
/**
* 创建玩家
* @param array $data
* @return array|mixed|Response
* @throws GameException|Exception
*/
public function createPlayer(array $data = [])
{
$params = [
'Account' => $data['uuid'],
'AgentId' => $this->appId
];
$signature = $this->createSign($params);
$params['Key'] = $signature;
$result = doFormCurl($this->createUrl('CreateMember'), $params);
if (!in_array($result['ErrorCode'],[0,101])) {
Log::error($result['Message'], ['res' => $result]);
throw new GameException($result['Message'], 0);
}
$playerGamePlatform = new PlayerGamePlatform();
$playerGamePlatform->player_id = $this->player->id;
$playerGamePlatform->platform_id = $this->platform->id;
$playerGamePlatform->player_name = $data['name'];
$playerGamePlatform->player_code = $data['uuid'];
$playerGamePlatform->save();
$this->loginId = $playerGamePlatform->player_code;
return $result;
}
/**
* 获取玩家
* @return array|mixed|Response
* @throws GameException|Exception
*/
public function getPlayer()
{
$params = [
'Accounts' => $this->loginId,
'AgentId' => $this->appId
];
$signature = $this->createSign($params);
$params['Key'] = $signature;
$result = doFormCurl($this->createUrl('GetMemberInfo'), $params);
if ($result['ErrorCode'] != 0) {
throw new GameException($result['Message'], 0);
}
return $result;
}
/**
* 玩家进入游戏
* @param array $data
* @return string
* @throws GameException|\think\Exception
*/
public function login(array $data = []): string
{
$params = [
'Account'=> $this->loginId,
'GameId'=> intval($data['gameCode']),
'Lang'=> $data['lang'],
'AgentId' => $this->appId
];
$signature = $this->createSign($params);
$params['Key'] = $signature;
$result = doFormCurl($this->createUrl('LoginWithoutRedirect'), $params);
if ($result['ErrorCode'] == 0 && !empty($result['Data'])) {
$link = $result['Data'];
}else{
throw new GameException($result['Message'], 0);
}
return $link;
}
/**
* 获取玩家游戏平台余额
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getBalance()
{
$params = [
'Accounts' => $this->loginId,
'AgentId' => $this->appId
];
$signature = $this->createSign($params);
$params['Key'] = $signature;
$result = doFormCurl($this->createUrl('GetMemberInfo'), $params);
if ($result['ErrorCode'] != 0) {
throw new GameException('JiLi System Error,Please contact the administrator', 0);
}
return bcdiv($result['Data'][0]['Balance'], 100 , 2);
}
/**
* 玩家钱包转入游戏平台
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function balanceTransferOut()
{
return $this->setBalanceTransfer(PlayerWalletTransfer::TYPE_IN, $this->player->wallet->money);
}
/**
* 游戏平台转入玩家钱包
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function balanceTransferIn()
{
$balance = $this->getBalance();
return $this->setBalanceTransfer(PlayerWalletTransfer::TYPE_OUT, $balance);
}
/**
* 轉帳進出額度
* @param $type
* @param float $amount
* @param float $reward
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
protected function setBalanceTransfer($type, float $amount = 0, float $reward = 0)
{
if ($type == 2 && $amount == 0 && $reward == 0) {
throw new GameException('转出/入金额错误', 0);
}
$params = [
'Account' => $this->loginId,
'TransactionId' => createOrderNo(),
'Amount' => $amount,
'TransferType' => $type,
'AgentId' => $this->appId
];
$signature = $this->createSign($params);
$params['Key'] = $signature;
$result = doFormCurl($this->createUrl('ExchangeTransferByAgentId'), $params);
if ($result['ErrorCode'] != 0) {
throw new GameException($result['Message'], 0);
}
if($type == 1){
$type = 2;
}else{
$type = 1;
}
// 记录玩家钱包转出转入记录
$this->createWalletTransfer($type, $amount, $reward, $result['Data']['TransactionId'] ?? '');
return $result;
}
/**
* 查询额度转移纪录
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getTransferList(string $startTime = '', string $endTime = '', int $page = 1, int $pageSize = 10)
{
$params = [
'StartTime' => $startTime,
'EndTime' => $endTime,
'Page' => $page,
'PageLimit' => $pageSize,
'AgentId' => $this->appId
];
$signature = $this->createSign($params);
$params['Key'] = $signature;
$result = doFormCurl($this->createUrl('GetTransferRecordByTime'), $params);
if ($result['ErrorCode'] != 0) {
throw new GameException($result['Message'], 0);
}
return $result['Data'];
}
/**
* 查询一笔额度转移纪录
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getTransferById(string $transactionId)
{
$params = [
'TransactionId' => $transactionId,
'AgentId' => $this->appId
];
$signature = $this->createSign($params);
$params['Key'] = $signature;
$result = doFormCurl($this->createUrl('CheckTransferByTransactionId'), $params);
if ($result['ErrorCode'] != 0) {
throw new GameException($result['Message'], 0);
}
return $result['Data'];
}
/**
* 查询游戏纪录
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getGameRecordList(string $startTime = '', string $endTime = '', int $page = 1, int $pageSize = 10000, int $gameId = null)
{
$params = [
'StartTime' => $startTime,
'EndTime' => $endTime,
'Page' => $page,
'PageLimit' => $pageSize,
'AgentId' => $this->appId
];
$signature = $this->createSign($params);
$params['Key'] = $signature;
if(!empty($gameId)){
$params['GameId'] = $gameId;
}
$result = doFormCurl($this->createUrl('GetBetRecordByTime'), $params);
if ($result['ErrorCode'] != 0) {
throw new GameException($result['Message'], 0);
}
return $result['Data'];
}
/**
* 查询指定游戏记录详情
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getGameRecordById(string $wagersId)
{
$params = [
'WagersId' => $wagersId,
'AgentId' => $this->appId
];
$signature = $this->createSign($params);
$params['Key'] = $signature;
$lang = locale();
$lang = Str::replace('_', '-', $lang);
$params['Lang'] = $lang;
$result = doFormCurl($this->createUrl('GetGameDetailUrl'), $params);
if ($result['ErrorCode'] != 0) {
throw new GameException($result['Message'], 0);
}
return $result['Data'];
}
/**
* 查询指定玩家游戏记录
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getUserGameRecord(string $startTime = '', string $endTime = '', int $page = 1, int $pageSize = 10, int $gameId = null, int $gameType = null)
{
$params = [
'StartTime' => $startTime,
'EndTime' => $endTime,
'Page' => $page,
'PageLimit' => $pageSize,
'Account' => $this->loginId,
'AgentId' => $this->appId
];
$signature = $this->createSign($params);
$params['Key'] = $signature;
if(!empty($gameId)){
$params['GameId'] = $gameId;
}
if(!empty($gameType)){
$params['GameType'] = $gameType;
}
$lang = locale();
$lang = Str::replace('_', '-', $lang);
$params['Lang'] = $lang;
$result = doFormCurl($this->createUrl('GetUserBetRecordByTime'), $params);
if ($result['ErrorCode'] != 0) {
throw new GameException($result['Message'], 0);
}
return $result['Data'];
}
/**
* 查询玩家游戏记录
* @param string $startTime
* @param string $endTime
* @return array
*/
public function handleOrderHistories(string $startTime = '', string $endTime = ''): array
{
try {
$page = 1;
$list = [];
$timezone = new DateTimeZone('Etc/GMT+4');
$start = new DateTime('-6 minutes', $timezone);
$end = new DateTime('-5 minutes', $timezone);
$startTime = $start->format('Y-m-d\TH:i:s');
$endTime = $end->format('Y-m-d\TH:i:s');
$data = $this->getGameRecordList($startTime, $endTime, $page);
if (!empty($data['Result'])) {
$total = $data['Pagination']['TotalNumber'] ?? 0;
if ($total > 0) {
$pageSize = 10000;
if (!empty($data['Result'])) {
foreach ($data['Result'] as $item) {
$list[] = [
'uuid' => $item['Account'],
'platform_id' => $this->platform->id,
'game_code' => $item['GameId'],
'bet' => abs(bcdiv($item['Turnover'], 100, 2)),
'win' => bcdiv($item['PayoffAmount'],100, 2),
'order_no' => $item['WagersId'],
'original_data' => json_encode($item,JSON_UNESCAPED_UNICODE),
'platform_action_at' => date('Y-m-d H:i:s', strtotime($item['SettlementTime'])),
'game_type' => $item['GameCategoryId'],
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
];
}
}
if ($total > $pageSize) {
$totalPages = ceil($total / $pageSize);
for ($page = 2; $page <= $totalPages; $page++) {
$nextData = $this->getGameRecordList($startTime,$endTime,$page);
if (!empty($nextData['Result'])) {
foreach ($nextData['Result'] as $item) {
$list[] = [
'uuid' => $item['Account'],
'platform_id' => $this->platform->id,
'game_code' => $item['GameId'],
'bet' => abs(bcdiv($item['Turnover'], 100, 2)),
'win' => bcdiv($item['PayoffAmount'],100, 2),
'order_no' => $item['WagersId'],
'original_data' => json_encode($item,JSON_UNESCAPED_UNICODE),
'platform_action_at' => date('Y-m-d H:i:s', strtotime($item['SettlementTime'])),
'game_type' => $item['GameCategoryId'],
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
];
}
}
}
}
}
}
} catch (Exception $e) {
return [];
}
return $list;
}
}

View File

@@ -0,0 +1,465 @@
<?php
namespace app\service\game;
use addons\webman\model\Game;
use addons\webman\model\GamePlatform;
use addons\webman\model\Player;
use addons\webman\model\PlayerGamePlatform;
use addons\webman\model\PlayerWalletTransfer;
use app\exception\GameException;
use Exception;
use support\Log;
use support\Response;
class JokerServiceInterface extends GameServiceFactory implements GameServiceInterface
{
public $method = 'POST';
private $apiDomain;
private $domain;
private $appId;
private $appSecret;
public $loginId;
public $gameType = [
'ECasino' => 'Casino',
'Fishing' => 'Fishing',
'Bingo' => 'Bingo',
'Slot' => 'Slot',
];
public $localGameType = [
'ECasino' => '2',
'Fishing' => '4',
'Bingo' => '8',
'Slot' => '1',
];
public $failCode = [
'S200' => '重復請求',
'F0001' => '無效的簽名',
'F0002' => '無效的SN',
'F0003' => '無效的參數',
'F0004' => '無效的貨幣',
'F0005' => '玩家已存在',
'F0006' => '玩家不存在',
'F0007' => '會員不存在',
'F0008' => '執行失敗',
'F0009' => '無效的方法',
'F0010' => '無效的用戶狀態',
'F0011' => '玩家狀態無需更新',
'F0012' => '超出數據範圍',
'F0013' => '無匹配數據',
'F0014' => '登入位置被禁止',
'F0015' => '分數不足夠',
'F0016' => '不支持禮碼',
'F0017' => '交易流水號不得重複',
'F0018' => '系統繁忙',
'F0019' => '日期時間各式錯誤',
'F0020' => '超出時間限制範圍開始時間與結束時間之間不能大於120分鐘',
'F0021' => '執行取消',
'M0001' => '系統維護',
'M0002' => '系統錯誤',
];
private $lang = [
'zh-CN' => 'zh_ch',
'en' => 'en_us',
'zh_tc' => 'zh_tc',
'th_th' => 'th_th',
'Ma_my' => 'Ma_my',
'vi_nam' => 'vi_nam',
'Fi_fi' => 'Fi_fi',
'Kr_ko' => 'Kr_ko',
'Hi_hi' => 'Hi_hi',
'My_mar' => 'My_mar',
'Br_po' => 'Br_po',
'cam_dia' => 'cam_dia', // 柬埔寨语
];
/**
* @param Player|null $player
* @param $type
* @throws Exception
*/
public function __construct($type, Player $player = null)
{
$config = config('game_platform.' . $type);
$this->appId = $config['app_id'];
$this->apiDomain = $config['api_domain'];
$this->domain = $config['domain'];
$this->appSecret = $config['app_secret'];
$this->platform = GamePlatform::query()->where('name', $type)->first();
if (!empty($player)) {
$this->player = $player;
$this->getLoginId();
}
}
/**
* 生成请求url
* @param $method
* @return string
*/
public function createUrl($signature): string
{
return $this->apiDomain."?AppID=".$this->appId."&Signature=".$signature;
}
public function createSign($params): string
{
ksort($params);
$signature = urlencode(base64_encode(hash_hmac("sha1", urldecode(http_build_query($params,'', '&')), $this->appSecret, TRUE)));
return $signature;
}
/**
* 更新游戏列表
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getSimpleGameList()
{
$params = [
'Method' => 'ListGames',
'Timestamp' => time(),
];
$signature = $this->createSign($params);
$result = doCurl($this->createUrl($signature), $params);
if (isset($result['ListGames'])) {
foreach ($result['ListGames'] as $game) {
Game::query()->updateOrCreate(
[
'platform_id' => $this->platform->id,
'game_code' => $game['GameCode'],
],
[
'platform_game_type' => $game['GameType'],
'game_type' => $this->localGameType[$game['GameType']],
'name' => $game['GameName'],
]
);
}
}else{
throw new GameException($result['Message'], 0);
}
return $result;
}
/**
* 获取游戏列表
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getGamesList()
{
$params = [
'Method' => 'ListGames',
'Timestamp' => time(),
];
$signature = $this->createSign($params);
$result = doCurl($this->createUrl($signature), $params);
if (isset($result['ListGames'])) {
return $result;
}else{
throw new GameException($result['Message'], 0);
}
}
/**
* 获取游戏列表
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function playFreeGame()
{
$params = [
'Method' => 'PLAY',
'Username' => $this->player->name,
'Timestamp' => time(),
];
$signature = $this->createSign($params);
$result = doCurl($this->createUrl($signature), $params);
if (isset($result['Token'])) {
return $forwardUrl = $this->domain."?token=".$result['Token']."&game=1jeqx59c7ztqg&mobile=false";
}else{
throw new GameException($result['Message'], 0);
}
}
/**
* 玩游戏
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function playGame()
{
$params = [
'Method' => 'PLAY',
'Username' => $this->player->name,
'RequestID' => createOrderNo(),
'Amount' => $amount,
'Timestamp' => time(),
];
$signature = $this->createSign($params);
$result = doCurl($this->createUrl($signature), $params);
if (isset($result['Token'])) {
return $forwardUrl = $this->domain."?token=".$result['Token']."&game=1jeqx59c7ztqg&mobile=false";
}else{
throw new GameException($result['Message'], 0);
}
}
/**
* @throws Exception
*/
protected function getLoginId()
{
/** @var PlayerGamePlatform $playerGamePlatform */
$playerGamePlatform = $this->player->playerGamePlatform->where('platform_id', $this->platform->id)->first();
if (!empty($playerGamePlatform)) {
return $this->loginId = $playerGamePlatform->player_code;
}
return $this->createPlayer([
'uuid' => $this->player->uuid,
'name' => $this->player->name,
]);
}
/**
* 创建玩家
* @param array $data
* @return array|mixed|Response
* @throws GameException|Exception
*/
public function createPlayer(array $data = [])
{
$params = [
'Method' => 'CU',
'Username' => $data['uuid'],
'Timestamp' => time(),
];
$signature = $this->createSign($params);
$res = doCurl($this->createUrl($signature), $params);
if (empty($res['Status']) || $res['Status'] != 'OK') {
Log::error($res['Message'], ['res' => $res]);
throw new GameException($res['Message'], 0);
}
$playerGamePlatform = new PlayerGamePlatform();
$playerGamePlatform->player_id = $this->player->id;
$playerGamePlatform->platform_id = $this->platform->id;
$playerGamePlatform->player_name = $data['name'];
$playerGamePlatform->player_code = $data['uuid'];
$playerGamePlatform->save();
$this->loginId = $playerGamePlatform->player_code;
return $res;
}
/**
* 获取玩家
* @return array|mixed|Response
* @throws GameException|Exception
*/
public function getPlayer()
{
$params = [
'ID' => createOrderNo(),
'Method' => 'GetPlayer',
'SN' => $this->appId,
'LoginId' => $this->loginId,
];
$params['Signature'] = $this->createSign($params);
$data = doCurl($this->createUrl('getPlayer'), $params);
if ($data['code'] != $this->successCode) {
throw new GameException($this->failCode[$data['code']], 0);
}
return $data;
}
/**
* 玩家进入游戏
* @param array $data
* @return string
* @throws GameException|\think\Exception
*/
public function login(array $data = []): string
{
$params = [
'Method' => 'PLAY',
'Username' => $this->loginId,
'RequestID' => createOrderNo(),
'Amount' => 0,
'Timestamp' => time(),
];
$signature = $this->createSign($params);
$res = doCurl($this->createUrl($signature), $params);
if (empty($res['Token'])) {
Log::error($res['Message'], ['res' => $res]);
throw new GameException($res['Message'], 0);
}
if (!empty($data['gameCode'])) {
$link = $this->domain.'?token='.$res['Token'].'&game='.$data['gameCode'].'&mobile=1&lang=en';
}else{
$link = $this->domain . ($res['data']['loginUrl'] ?? '') . '&' . $parametersValue;
}
return $link;
}
/**
* 设置用户状态
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function setPlayerStatus($status)
{
$params = [
'Method' => 'SS',
'Username' => $this->loginId,
'Status' => $status,
'Timestamp' => time(),
];
$signature = $this->createSign($params);
$res = doCurl($this->createUrl($signature), $params);
if ($res['Status'] != 'OK') {
throw new GameException($this->failCode[$res['Status']], 0);
}
return $res;
}
/**
* 获取玩家游戏平台余额
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getBalance()
{
$params = [
'Method' => 'GC',
'Username' => $this->loginId,
'Timestamp' => time(),
];
$signature = $this->createSign($params);
$res = doCurl($this->createUrl($signature), $params);
if (empty($res['Credit']) && !empty($res['Message'])) {
throw new GameException($res['Message'], 0);
}
return $res;
}
/**
* 玩家钱包转入游戏平台
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function balanceTransferOut()
{
return $this->setBalanceTransfer(PlayerWalletTransfer::TYPE_OUT, $this->player->wallet->money);
}
/**
* 游戏平台转入玩家钱包
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function balanceTransferIn()
{
$params = [
'Method' => 'WAC',
'Username' => $this->loginId,
'Timestamp' => time(),
'RequestID' => createOrderNo(),
];
$signature = $this->createSign($params);
$res = doCurl($this->createUrl($signature), $params);
if (empty($res['RequestID']) || $res['RequestID'] != $params['RequestID'] || $res['Amount'] == 0) {
throw new GameException($res['Message'], 0);
}
// 记录玩家钱包转出转入记录
$this->createWalletTransfer(PlayerWalletTransfer::TYPE_IN, $res['Amount'], 0, $res['RequestID'] ?? '');
return $res;
}
/**
* 轉帳進出額度
* 簽名密鑰方式 MD5 (Id+Method+SN+LoginId+APISecretKey)
* @param $type
* @param float $amount
* @param float $reward
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
protected function setBalanceTransfer($type, float $amount = 0, float $reward = 0)
{
if ($amount == 0 && $reward == 0) {
throw new GameException('转出/入金额错误', 0);
}
$params = [
'Method' => 'TC',
'Username' => $this->loginId,
'Timestamp' => time(),
'RequestID' => createOrderNo(),
'Amount' => $amount,
];
$signature = $this->createSign($params);
$res = doCurl($this->createUrl($signature), $params);
if (empty($res['RequestID']) || $res['RequestID'] != $params['RequestID']) {
// Log::error($res['Message'], ['res' => $res]);
throw new GameException($res['Message'], 0);
}
// 记录玩家钱包转出转入记录
$this->createWalletTransfer($type, $amount, $reward, $res['RequestID'] ?? '');
return $res;
}
/**
* 依據時間獲取遊戲紀錄
* MD5 (Id+Method+SN+StartTime+EndTime+APISecretKey)
* @param int $pageIndex
* @param string $startTime
* @param string $endTime
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function getGameRecordByTime(string $startTime = '', string $endTime = '')
{
$params = [
'Method' => 'TSM',
'StartDate' => $startTime,
'EndDate' => $endTime,
'NextId' => '',
'Delay' => 0,
'Timestamp' => time(),
];
$signature = $this->createSign($params);
$res = doCurl($this->createUrl($signature), $params);
// if (empty($res['data'])) {
// throw new GameException($res['Message'], 0);
// }
return $res;
}
}

View File

@@ -0,0 +1,389 @@
<?php
namespace app\service\game;
use addons\webman\model\GamePlatform;
use addons\webman\model\Player;
use addons\webman\model\PlayerGamePlatform;
use addons\webman\model\PlayerWalletTransfer;
use app\exception\GameException;
use Exception;
use support\Response;
class Kiss918ServiceInterface extends GameServiceFactory implements GameServiceInterface
{
public $method = 'POST';
private $apiDomain;
private $domain;
private $auth;
private $appSecret;
public $loginId;
public $password;
public $gameType = [
'2' => 'Casino',
'5' => 'Fishing',
'8' => 'Bingo',
'1' => 'Slot',
];
public $localGameType = [
'2' => '2',//赌场
'5' => '4',//捕鱼
'8' => '2',//宾果
'1' => '1',//斯洛
];
public $failCode = [
'S200' => '重復請求',
'F0001' => '無效的簽名',
'F0002' => '無效的SN',
'F0003' => '無效的參數',
'F0004' => '無效的貨幣',
'F0005' => '玩家已存在',
'F0006' => '玩家不存在',
'F0007' => '會員不存在',
'F0008' => '執行失敗',
'F0009' => '無效的方法',
'F0010' => '無效的用戶狀態',
'F0011' => '玩家狀態無需更新',
'F0012' => '超出數據範圍',
'F0013' => '無匹配數據',
'F0014' => '登入位置被禁止',
'F0015' => '分數不足夠',
'F0016' => '不支持禮碼',
'F0017' => '交易流水號不得重複',
'F0018' => '系統繁忙',
'F0019' => '日期時間各式錯誤',
'F0020' => '超出時間限制範圍開始時間與結束時間之間不能大於120分鐘',
'F0021' => '執行取消',
'M0001' => '系統維護',
'M0002' => '系統錯誤',
];
private $admin_user;
/**
* @param Player|null $player
* @param $type
* @throws Exception
*/
public function __construct($type, Player $player = null)
{
$config = config('game_platform.' . $type);
$this->auth = $config['auth'];
$this->admin_user = $config['admin_user'];
$this->apiDomain = $config['api_domain'];
$this->recordApi = $config['record_api'];
$this->domain = $config['domain'];
$this->appSecret = $config['app_secret'];
$this->platform = GamePlatform::query()->where('name', $type)->first();
if (!empty($player)) {
$this->player = $player;
$this->getLoginId();
}
}
public function createSign($params): string
{
$str = implode('', $params);
return strtoupper(md5(strtolower($str)));
}
/**
* 更新游戏列表
* @return false
*/
public function getSimpleGameList(): bool
{
return false;
}
/**
* 获取玩家ID
* @throws Exception
*/
protected function getLoginId()
{
/** @var PlayerGamePlatform $playerGamePlatform */
$playerGamePlatform = PlayerGamePlatform::query()->where('platform_id', $this->platform->id)->where('player_id',$this->player->id)->first();
if (!empty($playerGamePlatform)) {
$this->password = $playerGamePlatform->player_password;
return $this->loginId = $playerGamePlatform->player_code;
}
return $this->createPlayer([
'uuid' => $this->player->uuid,
'name' => $this->player->name,
]);
}
/**
* 创建玩家
* @param array $data
* @return array|mixed|Response
* @throws GameException|Exception
*/
public function createPlayer(array $data = [])
{
$time = round(microtime(true)*1000);
$randomParams = [
'action' => 'RandomUserName',
'userName' => $this->admin_user,
'time' => $time,
'authcode' => $this->auth,
];
$signature = $this->createSign([$this->auth, $randomParams['userName'], $time ,$this->appSecret]);
$randomParams['sign'] = $signature;
$randomUser = dogGetCurl($this->apiDomain.'ashx/account/account.ashx', $randomParams);;
if(!$randomUser['success'] || empty($randomUser['account'])){
throw new GameException($randomUser['msg'], 0);
}
$password = uniqid();
$params = [
'action' => 'addUser',
'agent' => $this->admin_user,
'PassWd' => $password,
'pwdtype' => 1,
'userName' => $randomUser['account'],
'Name' => $data['name'],
'Tel' => $this->player->phone ?? '',
'Memo' => $data['name'],
'UserType' => 1,
'time' => $time,
'authcode' => $this->auth,
];
$signature = $this->createSign([$this->auth, $params['userName'], $time ,$this->appSecret]);
$params['sign'] = $signature;
$result = dogGetCurl($this->apiDomain.'ashx/account/account.ashx', $params);;
if ($result['code'] == 0 && $result['success']) {
$playerGamePlatform = new PlayerGamePlatform();
$playerGamePlatform->player_id = $this->player->id;
$playerGamePlatform->platform_id = $this->platform->id;
$playerGamePlatform->player_name = $data['name'];
$playerGamePlatform->player_code = $randomUser['account'];
$playerGamePlatform->player_password = $password;
$playerGamePlatform->save();
$this->loginId = $playerGamePlatform->player_code;
$this->password = $playerGamePlatform->player_password;
return $result;
}else{
throw new GameException($result['msg'], 0);
}
}
/**
* 获取玩家
* @return array|mixed|Response
* @throws Exception
*/
public function getPlayer()
{
$time = round(microtime(true)*1000);
$params = [
'action' => 'getUserInfo',
'userName' => $this->loginId,
'time' => $time,
'authcode' => $this->auth,
];
$signature = $this->createSign([$this->auth, $this->loginId, $time ,$this->appSecret]);
$params['sign'] = $signature;
$result = dogGetCurl($this->apiDomain.'ashx/account/account.ashx', $params);
if($result['success']){
return $result;
}else{
throw new GameException('Kiss 918 System Error,Please contact the administrator', 0);
}
}
/**
* 玩家进入游戏
* @param array $data
* @return array
*/
public function login(array $data = []): array
{
return ['url' => 'https://yop1.918kiss.com/', 'account' => $this->loginId, 'password' => $this->password];
}
/**
* 获取玩家游戏平台余额
* @return string|null
* @throws Exception
*/
public function getBalance(): string
{
$playerInfo = $this->getPlayer();
return $playerInfo['MoneyNum'];
}
/**
* 玩家钱包转入游戏平台
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function balanceTransferOut()
{
if($this->player->wallet->money <= 0){
return true;
}
return $this->setBalanceTransfer(PlayerWalletTransfer::TYPE_OUT, $this->player->wallet->money);
}
/**
* 游戏平台转入玩家钱包
* @return array|mixed|null
* @throws Exception
*/
public function balanceTransferIn()
{
$balance = $this->getBalance();
if($balance == 0){
// 记录玩家钱包转出转入记录
$this->createWalletTransfer(PlayerWalletTransfer::TYPE_IN, 0, 0);
return true;
}
return $this->setBalanceTransfer(PlayerWalletTransfer::TYPE_IN, -$balance);
}
/**
* 轉帳進出額度
* @param $type
* @param float $amount
* @param float $reward
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
protected function setBalanceTransfer($type, float $amount = 0, float $reward = 0)
{
$time = round(microtime(true)*1000);
$params = [
'action' => 'setServerScore',
'orderid' => date('YmdHis').uniqid(),
'scoreNum' => $amount,
'userName' => $this->loginId,
'ActionUser' => $this->loginId,
'ActionIp' => request()->getRealIp(),
'time' => $time,
'authcode' => $this->auth,
];
$signature = $this->createSign([$this->auth, $this->loginId, $time ,$this->appSecret]);
$params['sign'] = $signature;
$result = dogGetCurl($this->apiDomain.'ashx/account/setScore.ashx', $params);
if($result['code'] != 0 || !$result['success']){
throw new GameException($result['msg'], 0);
}
// 记录玩家钱包转出转入记录
$this->createWalletTransfer($type, $amount, $reward, $params['orderid']);
return $result;
}
/**
* 查询游戏纪录
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getGameRecordList(string $startTime = '', string $endTime = '', $uuid, int $page = 1, int $pageSize = 1000)
{
$time = round(microtime(true)*1000);
$params = [
'pageIndex' => $page,
'pageSize' => $pageSize,
'userName' => $this->loginId,
'sDate' => $startTime,
'eDate' => $endTime,
'time' => $time,
'authcode' => $this->auth,
];
$signature = $this->createSign([$this->auth, $this->loginId, $time ,$this->appSecret]);
$params['sign'] = $signature;
$result = dogGetCurl($this->apiDomain, $params);
if($result['code'] == 0 || !$result['success']){
throw new GameException($result['msg'], 0);
}
return $result;
}
/**
* 查询全部玩家账单
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getReportRecord(string $startTime = '', string $endTime = '')
{
$time = round(microtime(true)*1000);
$params = [
'userName' => $this->admin_user,
'sDate' => $startTime,
'eDate' => $endTime,
'Type' => 'ServerTotalReport',
'time' => $time,
'authcode' => $this->auth,
];
$signature = $this->createSign([$this->auth, $this->admin_user, $time ,$this->appSecret]);
$params['sign'] = $signature;
$result = dogGetCurl($this->recordApi.'ashx/AgentTotalReport.ashx', $params);
if(!$result['success'] && !empty($result['results'])){
throw new GameException($result['msg'], 0);
}
return $result['results'];
}
/**
* 查询单个玩家单日输赢
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getAccountReport(string $startTime = '', string $endTime = '', $playerCode)
{
$time = round(microtime(true)*1000);
$params = [
'userName' => $playerCode,
'sDate' => $startTime,
'eDate' => $endTime,
'time' => $time,
'authcode' => $this->auth,
];
$signature = $this->createSign([$this->auth, $playerCode, $time ,$this->appSecret]);
$params['sign'] = $signature;
$result = dogGetCurl($this->apiDomain.'ashx/AccountReport.ashx', $params);
if($result['code'] == 0 || !$result['success']){
throw new GameException($result['msg'], 0);
}
return $result;
}
/**
* 查询玩家游戏记录
* @return array
*/
public function handleOrderHistories(): array
{
try {
$list = [];
$startTime = date('Y-m-d');
$endTime = date('Y-m-d');
$data = $this->getReportRecord($startTime, $endTime);
if (!empty($data)) {
foreach ($data as $item) {
$list[] = [
'player_code' => $item['Account'],
'platform_id' => $this->platform->id,
'game_code' => 'Kiss918',
'bet' => $item['press'],
'win' => $item['win'],
'order_no' => $item['idx'].date('Ymd'),
'original_data' => json_encode($item,JSON_UNESCAPED_UNICODE),
'platform_action_at' => $startTime,
'game_type' => 1,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
];
}
}
} catch (Exception $e) {
return [];
}
return $list;
}
}

View File

@@ -0,0 +1,435 @@
<?php
namespace app\service\game;
use addons\webman\model\Game;
use addons\webman\model\GamePlatform;
use addons\webman\model\Player;
use addons\webman\model\PlayerGamePlatform;
use addons\webman\model\PlayerWalletTransfer;
use app\exception\GameException;
use Exception;
use support\Log;
use support\Response;
class LionKingServiceInterface extends GameServiceFactory implements GameServiceInterface
{
public $method = 'POST';
private $apiDomain;
private $domain;
private $appId;
private $appSecret;
private $path = [
'createPlayer' => '/UserInfo/CreatePlayer',
'getPlayer' => '/UserInfo/GetPlayer',
'getGameList' => '/Game/GetGameList',
'getSimpleGameList' => '/Game/GetSimpleGameList',
'getLoginH5' => '/UserInfo/GetLoginH5',
'setPlayerStatus' => '/UserInfo/SetPlayerStatus',
'getBalance' => '/Account/GetBalance',
'setBalanceTransfer' => '/Account/SetBalanceTransfer',
'getGameRecordByTime' => '/Game/GetGameRecordByTime',
'getGameRecord' => '/Game/GetGameRecord',
'removeRecords' => '/Game/RemoveRecords',
];
public $successCode = 'S100';
public $loginId;
public $gameType = [
'10' => 'Slot',
'12' => 'Casino',
'13' => 'Arcade',
'16' => 'Fishing'
];
public $failCode = [
'S200' => '重復請求',
'F0001' => '無效的簽名',
'F0002' => '無效的SN',
'F0003' => '無效的參數',
'F0004' => '無效的貨幣',
'F0005' => '玩家已存在',
'F0006' => '玩家不存在',
'F0007' => '會員不存在',
'F0008' => '執行失敗',
'F0009' => '無效的方法',
'F0010' => '無效的用戶狀態',
'F0011' => '玩家狀態無需更新',
'F0012' => '超出數據範圍',
'F0013' => '無匹配數據',
'F0014' => '登入位置被禁止',
'F0015' => '分數不足夠',
'F0016' => '不支持禮碼',
'F0017' => '交易流水號不得重複',
'F0018' => '系統繁忙',
'F0019' => '日期時間各式錯誤',
'F0020' => '超出時間限制範圍開始時間與結束時間之間不能大於120分鐘',
'F0021' => '執行取消',
'M0001' => '系統維護',
'M0002' => '系統錯誤',
];
private $lang = [
'zh-CN' => 'zh_ch',
'en' => 'en_us',
'zh_tc' => 'zh_tc',
'th_th' => 'th_th',
'Ma_my' => 'Ma_my',
'vi_nam' => 'vi_nam',
'Fi_fi' => 'Fi_fi',
'Kr_ko' => 'Kr_ko',
'Hi_hi' => 'Hi_hi',
'My_mar' => 'My_mar',
'Br_po' => 'Br_po',
'cam_dia' => 'cam_dia', // 柬埔寨语
];
/**
* @param Player|null $player
* @param $type
* @throws Exception
*/
public function __construct($type, Player $player = null)
{
$config = config('game_platform.' . $type);
$this->appId = $config['app_id'];
$this->apiDomain = $config['api_domain'];
$this->domain = $config['domain'];
$this->appSecret = $config['app_secret'];
$this->platform = GamePlatform::query()->where('name', $type)->first();
if (!empty($player)) {
$this->player = $player;
$this->getLoginId();
}
}
/**
* @throws Exception
*/
protected function getLoginId()
{
/** @var PlayerGamePlatform $playerGamePlatform */
$playerGamePlatform = $this->player->playerGamePlatform->where('platform_id', $this->platform->id)->first();
if (!empty($playerGamePlatform)) {
return $this->loginId = $playerGamePlatform->player_code;
}
return $this->createPlayer([
'uuid' => $this->player->uuid,
'name' => $this->player->name,
]);
}
/**
* 创建玩家
* @param array $data
* @return array|mixed|Response
* @throws GameException|Exception
*/
public function createPlayer(array $data = [])
{
$params = [
'ID' => createOrderNo(),
'Method' => 'CreatePlayer',
'SN' => $this->appId,
'PlayerCode' => $data['uuid'],
];
$params['Signature'] = $this->createSign($params);
$params['PlayerName'] = $data['uuid'];
$res = doCurl($this->createUrl('createPlayer'), $params);
if ($res['code'] != $this->successCode) {
throw new GameException($this->failCode[$res['code']], 0);
}
$playerGamePlatform = new PlayerGamePlatform();
$playerGamePlatform->player_id = $this->player->id;
$playerGamePlatform->platform_id = $this->platform->id;
$playerGamePlatform->player_name = $data['name'];
$playerGamePlatform->player_code = $data['uuid'];
$playerGamePlatform->save();
$this->loginId = $playerGamePlatform->player_code;
return $res;
}
/**
* 创建玩家
* @return array|mixed|Response
* @throws GameException|Exception
*/
public function getPlayer()
{
$params = [
'ID' => createOrderNo(),
'Method' => 'GetPlayer',
'SN' => $this->appId,
'LoginId' => $this->loginId,
];
$params['Signature'] = $this->createSign($params);
$data = doCurl($this->createUrl('getPlayer'), $params);
if ($data['code'] != $this->successCode) {
throw new GameException($this->failCode[$data['code']], 0);
}
return $data;
}
/**
* 获取游戏摘要MD5 (id+method+sn+APlSecretKey)
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getSimpleGameList()
{
$params = [
'ID' => createOrderNo(),
'Method' => 'GetSimpleGameList',
'SN' => $this->appId,
];
$params['Signature'] = $this->createSign($params);
$data = doCurl($this->createUrl('getSimpleGameList'), $params);
if ($data['code'] != $this->successCode) {
throw new GameException($this->failCode[$data['code']], 0);
}
if (!empty($data['data']['games'])) {
foreach ($data['data']['games'] as $game) {
Game::query()->updateOrCreate(
[
'platform_id' => $this->platform->id,
'game_code' => $game['gameCode'],
],
[
'platform_game_type' => $game['type'],
]
);
}
}
return $data;
}
/**
* 获取游戏摘要MD5 (id+method+sn+APlSecretKey)
* @param array $data
* @return string
* @throws GameException|\think\Exception
*/
public function login(array $data = []): string
{
$params = [
'ID' => createOrderNo(),
'Method' => 'GetLoginH5',
'SN' => $this->appId,
'LoginId' => $this->loginId,
];
$params['Signature'] = $this->createSign($params);
$res = doCurl($this->createUrl('getLoginH5'), $params);
if ($res['code'] != $this->successCode) {
Log::error($this->failCode[$res['code']], ['res' => $res]);
throw new GameException($this->failCode[$res['code']], 0);
}
$responseH5 = [
'Language' => $this->lang[$data['lang']] ?? 'ch',
"GameId" => $data['gameCode'],
'CallbackAddress' => $data['callBackUrl'] ?? '',
"AppType" => $data['appType'] ?? 1,
"DeviceType" => 1,
];
$jsonString = json_encode($responseH5);
$parametersValue = base64_encode($jsonString);
$link = $this->domain . '/linkgame' . ($res['data']['loginUrl'] ?? '') . '&' . $parametersValue;
if (!empty($data['gameCode'])) {
$link .= '&' . $data['gameCode'];
}
return $link;
}
/**
* 設置登入玩家狀態。當玩家處於禁用狀態,該玩家無法在平台進行任何操作,如果玩家在遊戲進行中該玩家將會自動退出遊戲。
* 簽名密鑰方式 Md5(Id+Method+SN+LoginId+APISecretKey)
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function setPlayerStatus($status)
{
$params = [
'ID' => createOrderNo(),
'Method' => 'GetSimpleGameList',
'SN' => $this->appId,
'LoginId' => $this->loginId,
];
$params['Signature'] = $this->createSign($params);
$params['Status'] = $status;
$data = doCurl($this->createUrl('setPlayerStatus'), $params);
if ($data['code'] != $this->successCode) {
throw new GameException($this->failCode[$data['code']], 0);
}
return $data;
}
/**
* 獲取玩家餘額信息
* 簽名密鑰方式 MD5 (Id+Method+SN+LoginId+APISecretKey)
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getBalance()
{
$params = [
'ID' => createOrderNo(),
'Method' => 'GetBalance',
'SN' => $this->appId,
'LoginId' => $this->loginId,
];
$params['Signature'] = $this->createSign($params);
$data = doCurl($this->createUrl('getBalance'), $params);
if ($data['code'] != $this->successCode) {
throw new GameException($this->failCode[$data['code']], 0);
}
return $data;
}
/**
* 玩家钱包转入游戏平台
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function balanceTransferOut()
{
return $this->setBalanceTransfer(PlayerWalletTransfer::TYPE_OUT, $this->player->wallet->money);
}
/**
* 玩家钱包转入游戏平台
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function balanceTransferIn()
{
$balance = $this->getBalance();
return $this->setBalanceTransfer(PlayerWalletTransfer::TYPE_IN, !empty($balance['data']['result']) ? -$balance['data']['result'] : 0, !empty($balance['data']['reward']) ? -$balance['data']['reward'] : 0);
}
/**
* 轉帳進出額度
* 簽名密鑰方式 MD5 (Id+Method+SN+LoginId+APISecretKey)
* @param $type
* @param float $amount
* @param float $reward
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
protected function setBalanceTransfer($type, float $amount = 0, float $reward = 0)
{
if ($amount == 0 && $reward == 0) {
throw new GameException('转出/入金额错误', 0);
}
$params = [
'ID' => createOrderNo(),
'Method' => 'SetBalanceTransfer',
'SN' => $this->appId,
'LoginId' => $this->loginId,
];
$params['Signature'] = $this->createSign($params);
$params['Amount'] = $amount;
$params['Reward'] = $reward;
$res = doCurl($this->createUrl('setBalanceTransfer'), $params);
if ($res['code'] != $this->successCode) {
throw new GameException($this->failCode[$res['code']], 0);
}
// 记录玩家钱包转出转入记录
$this->createWalletTransfer($type, $amount, $reward, $res['data']['refId'] ?? '');
return $res;
}
/**
* 依據時間獲取遊戲紀錄
* MD5 (Id+Method+SN+StartTime+EndTime+APISecretKey)
* @param int $pageIndex
* @param string $startTime
* @param string $endTime
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function getGameRecordByTime(int $pageIndex = 1, string $startTime = '', string $endTime = '')
{
$params = [
'ID' => createOrderNo(),
'Method' => 'GetGameRecordByTime',
'SN' => $this->appId,
'StartTime' => $startTime,
'EndTime' => $endTime,
];
$pageSize = 500;
$params['Signature'] = $this->createSign($params);
$params['PageSize'] = $pageSize;
$params['PageIndex'] = $pageIndex;
$res = doCurl($this->createUrl('getGameRecordByTime'), $params);
if ($res['code'] != $this->successCode) {
throw new GameException($this->failCode[$res['code']], 0);
}
return $res;
}
/**
* 獲取玩家遊戲歷史紀錄
* MD5 (Id+Method+SN+APISecretKey)
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function getGameRecord()
{
$params = [
'ID' => createOrderNo(),
'Method' => 'GetGameRecord',
'SN' => $this->appId,
];
$params['Signature'] = $this->createSign($params);
$res = doCurl($this->createUrl('getGameRecord'), $params);
if ($res['code'] != $this->successCode) {
throw new GameException($this->failCode[$res['code']], 0);
}
return $res;
}
/**
* 刪除遊戲紀錄
* MD5 (Id+Method+SN+IdsToBeRemoved+APISecretKey)
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function removeRecords($idsToBeRemoved)
{
$params = [
'ID' => createOrderNo(),
'Method' => 'RemoveRecords',
'SN' => $this->appId,
'IdsToBeRemoved' => implode(',', $idsToBeRemoved),
];
$params['Signature'] = $this->createSign($params);
$res = doCurl($this->createUrl('removeRecords'), $params);
if ($res['code'] != $this->successCode) {
throw new GameException($this->failCode[$res['code']], 0);
}
return $res;
}
public function createSign($params): string
{
return md5(implode('', $params) . $this->appSecret);
}
/**
* 生成请求url
* @param $method
* @return string
*/
public function createUrl($method): string
{
return $this->apiDomain . $this->path[$method];
}
}

View File

@@ -0,0 +1,444 @@
<?php
namespace app\service\game;
use addons\webman\model\Game;
use addons\webman\model\GamePlatform;
use addons\webman\model\Player;
use addons\webman\model\PlayerGamePlatform;
use addons\webman\model\PlayerWalletTransfer;
use app\exception\GameException;
use Exception;
use support\Log;
use support\Response;
class Lucky365ServiceInterface extends GameServiceFactory implements GameServiceInterface
{
public $method = 'POST';
private $apiDomain;
private $domain;
private $appId;
private $appSecret;
private $path = [
'createPlayer' => '/UserInfo/CreatePlayer',
'getPlayer' => '/UserInfo/GetPlayer',
'getGameList' => '/Game/GetGameList',
'getSimpleGameList' => '/Game/GetSimpleGameList',
'getLoginH5' => '/UserInfo/GetLoginH5',
'setPlayerStatus' => '/UserInfo/SetPlayerStatus',
'getBalance' => '/Account/GetBalance',
'setBalanceTransfer' => '/Account/SetBalanceTransfer',
'getGameRecordByTime' => '/Game/GetGameRecordByTime',
'getGameRecord' => '/Game/GetGameRecord',
'removeRecords' => '/Game/RemoveRecords',
];
public $successCode = 'S100';
public $loginId;
public $gameType = [
'10' => 'Slot',
'12' => 'Casino',
'13' => 'Arcade',
'16' => 'Fishing'
];
public $localGameType = [
'10' => '1',
'12' => '2',
'13' => '3',
'16' => '4',
];
public $failCode = [
'S200' => '重復請求',
'F0001' => '無效的簽名',
'F0002' => '無效的SN',
'F0003' => '無效的參數',
'F0004' => '無效的貨幣',
'F0005' => '玩家已存在',
'F0006' => '玩家不存在',
'F0007' => '會員不存在',
'F0008' => '執行失敗',
'F0009' => '無效的方法',
'F0010' => '無效的用戶狀態',
'F0011' => '玩家狀態無需更新',
'F0012' => '超出數據範圍',
'F0013' => '無匹配數據',
'F0014' => '登入位置被禁止',
'F0015' => '分數不足夠',
'F0016' => '不支持禮碼',
'F0017' => '交易流水號不得重複',
'F0018' => '系統繁忙',
'F0019' => '日期時間各式錯誤',
'F0020' => '超出時間限制範圍開始時間與結束時間之間不能大於120分鐘',
'F0021' => '執行取消',
'M0001' => '系統維護',
'M0002' => '系統錯誤',
];
private $lang = [
'zh-CN' => 'zh_ch',
'en' => 'en_us',
'zh_tc' => 'zh_tc',
'th_th' => 'th_th',
'Ma_my' => 'Ma_my',
'vi_nam' => 'vi_nam',
'Fi_fi' => 'Fi_fi',
'Kr_ko' => 'Kr_ko',
'Hi_hi' => 'Hi_hi',
'My_mar' => 'My_mar',
'Br_po' => 'Br_po',
'cam_dia' => 'cam_dia', // 柬埔寨语
];
/**
* @param Player|null $player
* @param $type
* @throws Exception
*/
public function __construct($type, Player $player = null)
{
$config = config('game_platform.' . $type);
$this->appId = $config['app_id'];
$this->apiDomain = $config['api_domain'];
$this->domain = $config['domain'];
$this->appSecret = $config['app_secret'];
$this->platform = GamePlatform::query()->where('name', $type)->first();
if (!empty($player)) {
$this->player = $player;
$this->getLoginId();
}
}
/**
* @throws Exception
*/
protected function getLoginId()
{
/** @var PlayerGamePlatform $playerGamePlatform */
$playerGamePlatform = PlayerGamePlatform::query()->where('platform_id', $this->platform->id)->where('player_id',$this->player->id)->first();
if (!empty($playerGamePlatform)) {
return $this->loginId = $playerGamePlatform->player_code;
}
return $this->createPlayer([
'uuid' => $this->player->uuid,
'name' => $this->player->name,
]);
}
/**
* 创建玩家
* @param array $data
* @return array|mixed|Response
* @throws GameException|Exception
*/
public function createPlayer(array $data = [])
{
$params = [
'ID' => createOrderNo(),
'Method' => 'CreatePlayer',
'SN' => $this->appId,
'PlayerCode' => $data['uuid'],
];
$params['Signature'] = $this->createSign($params);
$params['PlayerName'] = $data['uuid'];
$res = doCurl($this->createUrl('createPlayer'), $params);
if ($res['code'] != $this->successCode) {
throw new GameException($this->failCode[$res['code']], 0);
}
$playerGamePlatform = new PlayerGamePlatform();
$playerGamePlatform->player_id = $this->player->id;
$playerGamePlatform->platform_id = $this->platform->id;
$playerGamePlatform->player_name = $data['name'];
$playerGamePlatform->player_code = $data['uuid'];
$playerGamePlatform->save();
$this->loginId = $playerGamePlatform->player_code;
return $res;
}
/**
* 创建玩家
* @return array|mixed|Response
* @throws GameException|Exception
*/
public function getPlayer()
{
$params = [
'ID' => createOrderNo(),
'Method' => 'GetPlayer',
'SN' => $this->appId,
'LoginId' => $this->loginId,
];
$params['Signature'] = $this->createSign($params);
$data = doCurl($this->createUrl('getPlayer'), $params);
if ($data['code'] != $this->successCode) {
throw new GameException($this->failCode[$data['code']], 0);
}
return $data;
}
/**
* 获取游戏摘要MD5 (id+method+sn+APlSecretKey)
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getSimpleGameList()
{
$params = [
'ID' => createOrderNo(),
'Method' => 'GetSimpleGameList',
'SN' => $this->appId,
];
$params['Signature'] = $this->createSign($params);
$data = doCurl($this->createUrl('getSimpleGameList'), $params);
if ($data['code'] != $this->successCode) {
throw new GameException($this->failCode[$data['code']], 0);
}
if (!empty($data['data']['games'])) {
foreach ($data['data']['games'] as $game) {
Game::query()->updateOrCreate(
[
'platform_id' => $this->platform->id,
'game_code' => $game['gameCode'],
],
[
'platform_game_type' => $game['type'],
'game_type' => $this->localGameType[$game['type']],
'name' => $game['gameName'],
]
);
}
}
return $data;
}
/**
* 获取游戏摘要MD5 (id+method+sn+APlSecretKey)
* @param array $data
* @return string
* @throws GameException|\think\Exception
*/
public function login(array $data = []): string
{
$params = [
'ID' => createOrderNo(),
'Method' => 'GetLoginH5',
'SN' => $this->appId,
'LoginId' => $this->loginId,
];
$params['Signature'] = $this->createSign($params);
$res = doCurl($this->createUrl('getLoginH5'), $params);
if ($res['code'] != $this->successCode) {
Log::error($this->failCode[$res['code']], ['res' => $res]);
throw new GameException($this->failCode[$res['code']], 0);
}
$responseH5 = [
'Language' => $this->lang[$data['lang']] ?? 'ch',
"GameId" => $data['gameCode'],
'CallbackAddress' => $data['callBackUrl'] ?? '',
"AppType" => $data['appType'] ?? 1,
"DeviceType" => 1,
];
$jsonString = json_encode($responseH5);
$parametersValue = base64_encode($jsonString);
if (!empty($data['gameCode'])) {
$link = $this->domain .'/linkgame'. ($res['data']['loginUrl'] ?? '') . '&' . $parametersValue . '&' . $data['gameCode'];
}else{
$link = $this->domain . ($res['data']['loginUrl'] ?? '') . '&' . $parametersValue;
}
return $link;
}
/**
* 設置登入玩家狀態。當玩家處於禁用狀態,該玩家無法在平台進行任何操作,如果玩家在遊戲進行中該玩家將會自動退出遊戲。
* 簽名密鑰方式 Md5(Id+Method+SN+LoginId+APISecretKey)
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function setPlayerStatus($status)
{
$params = [
'ID' => createOrderNo(),
'Method' => 'GetSimpleGameList',
'SN' => $this->appId,
'LoginId' => $this->loginId,
];
$params['Signature'] = $this->createSign($params);
$params['Status'] = $status;
$data = doCurl($this->createUrl('setPlayerStatus'), $params);
if ($data['code'] != $this->successCode) {
throw new GameException($this->failCode[$data['code']], 0);
}
return $data;
}
/**
* 獲取玩家餘額信息
* 簽名密鑰方式 MD5 (Id+Method+SN+LoginId+APISecretKey)
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getBalance()
{
$params = [
'ID' => createOrderNo(),
'Method' => 'GetBalance',
'SN' => $this->appId,
'LoginId' => $this->loginId,
];
$params['Signature'] = $this->createSign($params);
$data = doCurl($this->createUrl('getBalance'), $params);
if ($data['code'] != $this->successCode) {
throw new GameException('Lucky365 System Error,Please contact the administrator', 0);
}
return $data;
}
/**
* 玩家钱包转入游戏平台
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function balanceTransferOut()
{
return $this->setBalanceTransfer(PlayerWalletTransfer::TYPE_OUT, $this->player->wallet->money);
}
/**
* 玩家钱包转入游戏平台
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function balanceTransferIn()
{
$balance = $this->getBalance();
return $this->setBalanceTransfer(PlayerWalletTransfer::TYPE_IN, !empty($balance['data']['result']) ? -$balance['data']['result'] : 0, !empty($balance['data']['reward']) ? -$balance['data']['reward'] : 0);
}
/**
* 轉帳進出額度
* 簽名密鑰方式 MD5 (Id+Method+SN+LoginId+APISecretKey)
* @param $type
* @param float $amount
* @param float $reward
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
protected function setBalanceTransfer($type, float $amount = 0, float $reward = 0)
{
$params = [
'ID' => createOrderNo(),
'Method' => 'SetBalanceTransfer',
'SN' => $this->appId,
'LoginId' => $this->loginId,
];
$params['Signature'] = $this->createSign($params);
$params['Amount'] = $amount;
$params['Reward'] = $reward;
$res = doCurl($this->createUrl('setBalanceTransfer'), $params);
if ($res['code'] != $this->successCode) {
throw new GameException($this->failCode[$res['code']], 0);
}
// 记录玩家钱包转出转入记录
$this->createWalletTransfer($type, $amount, $reward, $res['data']['refId'] ?? '');
return $res;
}
/**
* 依據時間獲取遊戲紀錄
* MD5 (Id+Method+SN+StartTime+EndTime+APISecretKey)
* @param int $pageIndex
* @param string $startTime
* @param string $endTime
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function getGameRecordByTime(int $pageIndex = 1, string $startTime = '', string $endTime = '')
{
$params = [
'ID' => createOrderNo(),
'Method' => 'GetGameRecordByTime',
'SN' => $this->appId,
'StartTime' => $startTime,
'EndTime' => $endTime,
];
$pageSize = 500;
$params['Signature'] = $this->createSign($params);
$params['PageSize'] = $pageSize;
$params['PageIndex'] = $pageIndex;
$res = doCurl($this->createUrl('getGameRecordByTime'), $params);
if ($res['code'] != $this->successCode) {
throw new GameException($this->failCode[$res['code']], 0);
}
return $res;
}
/**
* 獲取玩家遊戲歷史紀錄
* MD5 (Id+Method+SN+APISecretKey)
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function getGameRecord()
{
$params = [
'ID' => createOrderNo(),
'Method' => 'GetGameRecord',
'SN' => $this->appId,
];
$params['Signature'] = $this->createSign($params);
$res = doCurl($this->createUrl('getGameRecord'), $params);
if ($res['code'] != $this->successCode) {
throw new GameException($this->failCode[$res['code']], 0);
}
return $res;
}
/**
* 刪除遊戲紀錄
* MD5 (Id+Method+SN+IdsToBeRemoved+APISecretKey)
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function removeRecords($idsToBeRemoved)
{
$params = [
'ID' => createOrderNo(),
'Method' => 'RemoveRecords',
'SN' => $this->appId,
'IdsToBeRemoved' => implode(',', $idsToBeRemoved),
];
$params['Signature'] = $this->createSign($params);
$res = doCurl($this->createUrl('removeRecords'), $params);
if ($res['code'] != $this->successCode) {
throw new GameException($this->failCode[$res['code']], 0);
}
return $res;
}
public function createSign($params): string
{
return md5(implode('', $params) . $this->appSecret);
}
/**
* 生成请求url
* @param $method
* @return string
*/
public function createUrl($method): string
{
return $this->apiDomain . $this->path[$method];
}
}

View File

@@ -0,0 +1,411 @@
<?php
namespace app\service\game;
use addons\webman\model\Game;
use addons\webman\model\GamePlatform;
use addons\webman\model\Player;
use addons\webman\model\PlayerGamePlatform;
use addons\webman\model\PlayerWalletTransfer;
use app\exception\GameException;
use DateTime;
use DateTimeZone;
use Exception;
use Illuminate\Support\Str;
use support\Log;
use support\Response;
class MarioClubServiceInterface extends GameServiceFactory implements GameServiceInterface
{
public $method = 'POST';
private $apiDomain;
private $appId;
private $appSecret;
public $loginId;
public $gameType = [
'2' => 'Casino',
'5' => 'Fishing',
'8' => 'Bingo',
'1' => 'Slot',
];
public $localGameType = [
'FISHING' => '4',//捕鱼
'SLOT' => '1',//斯洛
'1' => '1',
'4' => '4',
];
public $failCode = [
'S200' => '重復請求',
'F0001' => '無效的簽名',
'F0002' => '無效的SN',
'F0003' => '無效的參數',
'F0004' => '無效的貨幣',
'F0005' => '玩家已存在',
'F0006' => '玩家不存在',
'F0007' => '會員不存在',
'F0008' => '執行失敗',
'F0009' => '無效的方法',
'F0010' => '無效的用戶狀態',
'F0011' => '玩家狀態無需更新',
'F0012' => '超出數據範圍',
'F0013' => '無匹配數據',
'F0014' => '登入位置被禁止',
'F0015' => '分數不足夠',
'F0016' => '不支持禮碼',
'F0017' => '交易流水號不得重複',
'F0018' => '系統繁忙',
'F0019' => '日期時間各式錯誤',
'F0020' => '超出時間限制範圍開始時間與結束時間之間不能大於120分鐘',
'F0021' => '執行取消',
'M0001' => '系統維護',
'M0002' => '系統錯誤',
];
/**
* @param Player|null $player
* @param $type
* @throws Exception
*/
public function __construct($type, Player $player = null)
{
$config = config('game_platform.' . $type);
$this->appId = $config['app_id'];
$this->apiDomain = $config['api_domain'];
$this->appSecret = $config['app_secret'];
$this->platform = GamePlatform::query()->where('name', $type)->first();
if (!empty($player)) {
$this->player = $player;
$this->getLoginId();
}
}
/**
* 生成签名
* @param $time
* @return string
*/
public function createSign($time): string
{
return md5($this->appId.$time.$this->appSecret);
}
/**
* 更新游戏列表
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getSimpleGameList()
{
$time = time();
$params = [
'api_id' => $this->appId,
'timestamp' => $time,
];
$params['sign'] = $this->createSign($time);
$result = doFormCurl($this->apiDomain.'/getGames',$params);
if ($result['code'] == 0 && !empty($result['gameList'])) {
foreach ($result['gameList'] as $game) {
Game::query()->updateOrCreate(
[
'platform_id' => $this->platform->id,
'game_code' => $game['gameCode'],
],
[
'platform_game_type' => $game['gameType'],
'game_type' => $this->localGameType[$game['gameType']] ?? '',
'name' => $game['gameName'],
'game_image' => $game['gameIconUrl']
]
);
}
}else{
throw new GameException($result['Message'], 0);
}
return $result;
}
/**
* 获取游戏列表
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getGamesList()
{
$time = time();
$params = [
'api_id' => $this->appId,
'timestamp' => $time,
];
$params['sign'] = $this->createSign($time);
$result = doFormCurl($this->apiDomain.'/getGames',$params);
if ($result['code'] == 0 && !empty($result['gameList'])) {
return $result;
}else{
throw new GameException($result['Message'], 0);
}
}
/**
* 获取玩家ID
* @throws Exception
*/
protected function getLoginId()
{
/** @var PlayerGamePlatform $playerGamePlatform */
$playerGamePlatform = PlayerGamePlatform::query()->where('platform_id', $this->platform->id)->where('player_id',$this->player->id)->first();
if (!empty($playerGamePlatform)) {
return $this->loginId = $playerGamePlatform->player_code;
}
return $this->createPlayer([
'uuid' => $this->player->uuid,
'name' => $this->player->name,
]);
}
/**
* 创建玩家
* @param array $data
* @return array|mixed|Response
* @throws GameException|Exception
*/
public function createPlayer(array $data = [])
{
$time = time();
$params = [
'api_id' => $this->appId,
'timestamp' => $time,
'user_id' => $data['uuid'],
];
$params['sign'] = $this->createSign($time);
$result = doFormCurl($this->apiDomain.'/api/acc/created',$params);
if ($result['code'] != 0) {
Log::error($result['msg'], ['res' => $result]);
throw new GameException($result['msg'], 0);
}
$playerGamePlatform = new PlayerGamePlatform();
$playerGamePlatform->player_id = $this->player->id;
$playerGamePlatform->platform_id = $this->platform->id;
$playerGamePlatform->player_name = $data['name'];
$playerGamePlatform->player_code = $data['uuid'];
$playerGamePlatform->save();
$this->loginId = $playerGamePlatform->player_code;
return $result;
}
/**
* 获取玩家
* @return array|mixed|Response
* @throws GameException|Exception
*/
public function getPlayer()
{
$time = time();
$params = [
'api_id' => $this->appId,
'timestamp' => $time,
'user_id' => $this->loginId,
'language' => 'en',
];
$params['sign'] = $this->createSign($time);
$result = doFormCurl($this->apiDomain.'/api/acc/gameLogIn',$params);
if ($result['code'] != 0) {
throw new GameException($result['msg'], 0);
}
return $result;
}
/**
* 玩家进入游戏
* @param array $data
* @return string
* @throws GameException|\think\Exception
*/
public function login(array $data = []): string
{
$time = time();
$params = [
'api_id' => $this->appId,
'timestamp' => $time,
'user_id' => $this->loginId,
'game_code' => $data['gameCode'],
'language' => 'en',
];
$params['sign'] = $this->createSign($time);
$result = doFormCurl($this->apiDomain.'/api/acc/gameLogIn',$params);
if ($result['code'] == 0 && !empty($result['gameUrl'])) {
$link = $result['gameUrl'];
}else{
throw new GameException($result['msg'], 0);
}
return $link;
}
/**
* 获取玩家游戏平台余额
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getBalance()
{
$time = time();
$params = [
'api_id' => $this->appId,
'timestamp' => $time,
'user_id' => $this->loginId,
];
$params['sign'] = $this->createSign($time);
$result = doFormCurl($this->apiDomain.'/api/acc/getBalance',$params);
if ($result['code'] != 0) {
throw new GameException('MarioClub System Error,Please contact the administrator', 0);
}
return $result['balance'];
}
/**
* 玩家钱包转入游戏平台
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function balanceTransferOut()
{
return $this->setBalanceTransfer(PlayerWalletTransfer::TYPE_OUT, $this->player->wallet->money);
}
/**
* 游戏平台转入玩家钱包
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function balanceTransferIn()
{
$balance = $this->getBalance();
if($balance == 0){
// 记录玩家钱包转出转入记录
$this->createWalletTransfer(PlayerWalletTransfer::TYPE_IN, 0, 0);
return true;
}
return $this->setBalanceTransfer(PlayerWalletTransfer::TYPE_IN, $balance);
}
/**
* 轉帳進出額度
* @param $type
* @param float $amount
* @param float $reward
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
protected function setBalanceTransfer($type, float $amount = 0, float $reward = 0)
{
if ($type == 1) {
$action = 'deposit';
} else {
$action = 'withdraw';
}
$time = time();
$params = [
'api_id' => $this->appId,
'timestamp' => $time,
'user_id' => $this->loginId,
'amount' => $amount,
'action' => $action,
];
$params['sign'] = $this->createSign($time);
$result = doFormCurl($this->apiDomain.'/api/acc/updateBalance',$params);
if ($result['code'] != 0) {
throw new GameException($result['msg'], 0);
}
// 记录玩家钱包转出转入记录
$this->createWalletTransfer($type, $amount, $reward, $result['transId'] ?? '');
return $result;
}
/**
* 查询额度转移纪录
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getTransferList(string $startTime = '', string $endTime = '', int $page = 1, int $pageSize = 10)
{
$time = time();
$params = [
'api_id' => $this->appId,
'timestamp' => $time,
'user_id' => $this->loginId,
'date_from' => date('d/m/Y', strtotime($startTime)),
'date_to' => date('d/m/Y', strtotime($endTime)),
];
$params['sign'] = $this->createSign($time);
$result = doFormCurl($this->apiDomain.'/api/acc/getTransactionLog',$params);
if ($result['code'] != 0) {
throw new GameException($result['msg'], 0);
}
return $result['walletLogDTO'];
}
/**
* 查询游戏纪录
* @param string $startTime
* @param string $endTime
* @return array
* @throws GameException
* @throws \think\Exception
*/
public function getGameRecordList(string $startTime = '', string $endTime = ''): array
{
$time = time();
$params = [
'api_id' => $this->appId,
'timestamp' => $time,
'date_from' => $startTime,
'date_to' => $endTime,
];
$params['sign'] = $this->createSign($time);
$result = doFormCurl($this->apiDomain.'/api/acc/getGameLog',$params);
if ($result['code'] != 0) {
throw new GameException($result['msg'], 0);
}
return $result['userGameLogDTO'];
}
/**
* 查询玩家游戏记录
* @return array
*/
public function handleOrderHistories(): array
{
try {
$list = [];
$startTime = date('d/m/Y H:i:s', strtotime('-3 minutes'));
$endTime = date('d/m/Y H:i:s', strtotime('-2 minutes'));
$data = $this->getGameRecordList($startTime, $endTime);
if (!empty($data)) {
foreach ($data as $item) {
$list[] = [
'uuid' => $item['loginId'],
'platform_id' => $this->platform->id,
'game_code' => $item['gameCode'],
'bet' => $item['betAmount'],
'win' => bcadd($item['betAmount'], $item['winAmount'], 2),
'order_no' => $item['logId'],
'original_data' => json_encode($item,JSON_UNESCAPED_UNICODE),
'platform_action_at' => date('Y-m-d H:i:s', $item['createdDate']/1000),
'game_type' => $item['gameTypeId'],
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
];
}
}
} catch (Exception $e) {
return [];
}
return $list;
}
}

View File

@@ -0,0 +1,472 @@
<?php
namespace app\service\game;
use addons\webman\model\GamePlatform;
use addons\webman\model\Player;
use addons\webman\model\PlayerGamePlatform;
use addons\webman\model\PlayerWalletTransfer;
use app\exception\GameException;
use Exception;
use support\Response;
class MeGa888ServiceInterface extends GameServiceFactory implements GameServiceInterface
{
public $method = 'POST';
private $apiDomain;
private $appId;
public $appSecret;
private $sn;
public $loginId;
public $password;
public $gameType = [
'2' => 'Casino',
'5' => 'Fishing',
'8' => 'Bingo',
'1' => 'Slot',
];
public $localGameType = [
'2' => '2',//赌场
'5' => '4',//捕鱼
'8' => '8',//宾果
'1' => '1',//斯洛
];
public $failCode = [
'S200' => '重復請求',
'F0001' => '無效的簽名',
'F0002' => '無效的SN',
'F0003' => '無效的參數',
'F0004' => '無效的貨幣',
'F0005' => '玩家已存在',
'F0006' => '玩家不存在',
'F0007' => '會員不存在',
'F0008' => '執行失敗',
'F0009' => '無效的方法',
'F0010' => '無效的用戶狀態',
'F0011' => '玩家狀態無需更新',
'F0012' => '超出數據範圍',
'F0013' => '無匹配數據',
'F0014' => '登入位置被禁止',
'F0015' => '分數不足夠',
'F0016' => '不支持禮碼',
'F0017' => '交易流水號不得重複',
'F0018' => '系統繁忙',
'F0019' => '日期時間各式錯誤',
'F0020' => '超出時間限制範圍開始時間與結束時間之間不能大於120分鐘',
'F0021' => '執行取消',
'M0001' => '系統維護',
'M0002' => '系統錯誤',
];
/**
* @param Player|null $player
* @param $type
* @throws Exception
*/
public function __construct($type, Player $player = null)
{
$config = config('game_platform.' . $type);
$this->appId = $config['app_id'];
$this->apiDomain = $config['api_domain'];
$this->appSecret = $config['app_secret'];
$this->sn = $config['sn'];
$this->platform = GamePlatform::query()->where('name', $type)->first();
if (!empty($player)) {
$this->player = $player;
$this->getLoginId();
}
}
/**
* 生成请求url
* @return string
*/
public function createUrl(): string
{
return $this->apiDomain;
}
//生成签名
public function createSign($params): string
{
$str = '';
foreach ($params as $v) {
$str .= $v;
}
return md5($str);
}
/**
* 生成请求数据
* @param $postData
* @param $method
* @return array
*/
function buildParams($postData, $method): array
{
return array(
"jsonrpc" => "2.0",
"method" => $method,
"params" => $postData,
"id" => $this->player->uuid ?? uniqid()
);
}
/**
* 获取下载地址
* @throws GameException|\think\Exception
*/
public function getDownload()
{
$params = [
'random' => $this->player->uuid ?? uniqid(),
'sn' => $this->sn,
'secretCode' => $this->appSecret,
];
$params['digest'] = $this->createSign($params);
$params['agentLoginId'] = $this->appId;
$postData = $this->buildParams($params, 'open.mega.app.url.download');
$result = doCurl($this->apiDomain,$postData);
if(!empty($result['result'])){
return $result['result'];
}else{
throw new GameException($result['error']['message'], 0);
}
}
/**
* 更新游戏列表
* @return false
*/
public function getSimpleGameList(): bool
{
return false;
}
/**
* 获取玩家ID
* @throws Exception
*/
protected function getLoginId()
{
/** @var PlayerGamePlatform $playerGamePlatform */
$playerGamePlatform = PlayerGamePlatform::query()->where('platform_id', $this->platform->id)->where('player_id',$this->player->id)->first();
if (!empty($playerGamePlatform)) {
$this->password = $playerGamePlatform->player_password;
return $this->loginId = $playerGamePlatform->player_code;
}
return $this->createPlayer([
'uuid' => $this->player->uuid,
'name' => $this->player->name,
]);
}
/**
* 创建玩家
* @param array $data
* @return array|mixed|Response
* @throws GameException|Exception
*/
public function createPlayer(array $data = [])
{
$params = [
'random' => $data['uuid'] ?? uniqid(),
'sn' => $this->sn,
'secretCode' => $this->appSecret,
];
$params['digest'] = $this->createSign($params);
$params['agentLoginId'] = $this->appId;
$params['nickname'] = $data['name'];
$postData = $this->buildParams($params, 'open.mega.user.create');
$result = doCurl($this->apiDomain,$postData);
if (!empty($result['result']) && empty($result['error'])) {
$playerGamePlatform = new PlayerGamePlatform();
$playerGamePlatform->player_id = $this->player->id;
$playerGamePlatform->platform_id = $this->platform->id;
$playerGamePlatform->player_name = $data['name'];
$playerGamePlatform->player_code = $result['result']['loginId'];
$playerGamePlatform->player_password = uniqid();
$playerGamePlatform->save();
$this->loginId = $playerGamePlatform->player_code;
$this->password = $playerGamePlatform->player_password;
return $result;
}else{
throw new GameException($result['error']['message'], 0);
}
}
/**
* 获取玩家信息
* @return array|mixed|Response
* @throws GameException|Exception
*/
public function getPlayer()
{
$params = [
'random' => $this->player->uuid ?? uniqid(),
'sn' => $this->sn,
'loginId' => $this->loginId,
'secretCode' => $this->appSecret,
];
$params['digest'] = $this->createSign($params);
$postData = $this->buildParams($params, 'open.mega.user.get');
$result = doCurl($this->apiDomain,$postData);
if (!empty($result['result']) && empty($result['error'])) {
return $result;
}else{
throw new GameException($result['error']['message'], 0);
}
}
/**
* 玩家进入游戏
* @param array $data
* @return array
* @throws GameException
* @throws \think\Exception
*/
public function login(array $data = []): array
{
return ['url' => $this->getDownload(), 'account' => $this->loginId, 'password' => $this->password];
}
/**
* 获取玩家游戏平台余额
* @throws GameException|\think\Exception
*/
public function getBalance()
{
$params = [
'random' => $this->player->uuid ?? uniqid(),
'sn' => $this->sn,
'loginId' => $this->loginId,
'secretCode' => $this->appSecret,
];
$params['digest'] = $this->createSign($params);
$postData = $this->buildParams($params, 'open.mega.balance.get');
$result = doCurl($this->apiDomain,$postData);
if (empty($result['error'])) {
return $result['result'];
}else{
throw new GameException('MeGa888 System Error,Please contact the administrator', 0);
}
}
/**
* 玩家钱包转入游戏平台
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function balanceTransferOut()
{
return $this->setBalanceTransfer(PlayerWalletTransfer::TYPE_OUT, $this->player->wallet->money);
}
/**
* 游戏平台转入玩家钱包
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function balanceTransferIn()
{
if($this->getBalance() == 0){
// 记录玩家钱包转出转入记录
$this->createWalletTransfer(PlayerWalletTransfer::TYPE_IN, 0, 0);
return true;
}
return $this->autoTransfer();
}
/**
* 轉帳進出額度
* @param $type
* @param float $amount
* @param float $reward
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
protected function setBalanceTransfer($type, float $amount = 0, float $reward = 0)
{
$params = [
'random' => $this->player->uuid ?? uniqid(),
'sn' => $this->sn,
'loginId' => $this->loginId,
'amount' => $amount,
'secretCode' => $this->appSecret,
];
$params['digest'] = $this->createSign($params);
$postData = $this->buildParams($params, 'open.mega.balance.transfer');
$result = doCurl($this->apiDomain,$postData);
if (!empty($result['error'])) {
throw new GameException($result['error']['message'], 0);
}
// 记录玩家钱包转出转入记录
$this->createWalletTransfer($type, $amount, $reward);
return $result;
}
/**
* 自动下分
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function autoTransfer()
{
$params = [
'random' => $this->player->uuid ?? uniqid(),
'sn' => $this->sn,
'loginId' => $this->loginId,
'secretCode' => $this->appSecret,
];
$params['digest'] = $this->createSign($params);
$postData = $this->buildParams($params, 'open.mega.balance.auto.transfer.out');
$result = doCurl($this->apiDomain,$postData);
if (!empty($result['error'])) {
throw new GameException($result['error']['message'], 0);
}
// 记录玩家钱包转出转入记录
$this->createWalletTransfer(PlayerWalletTransfer::TYPE_IN, $result['result'], 0);
return $result;
}
/**
* 查询额度转移纪录
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getTransferList(string $startTime = '', string $endTime = '', int $page = 1, int $pageSize = 15)
{
$params = [
'random' => $this->player->uuid ?? uniqid(),
'sn' => $this->sn,
'secretCode' => $this->appSecret,
];
$params['digest'] = $this->createSign($params);
$params['loginId'] = $this->loginId;
$params['agentLoginId'] = $this->appId;
$params['startTime'] = $startTime;
$params['endTime'] = $endTime;
$params['timeZone'] = 1;
$postData = $this->buildParams($params, 'open.mega.balance.transfer.query');
$result = doCurl($this->apiDomain,$postData);
if (!empty($result['error'])) {
throw new GameException($result['error']['message'], 0);
}
return $result;
}
/**
* 查询玩家游戏记录
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getUserGameRecord(string $startTime = '', string $endTime = '')
{
$params = [
'random' => $this->player->uuid ?? uniqid(),
'sn' => $this->sn,
'loginId' => $this->loginId,
'secretCode' => $this->appSecret,
];
$params['digest'] = $this->createSign($params);
$params['startTime'] = $startTime;
$params['endTime'] = $endTime;
$params['timeZone'] = 1;
$postData = $this->buildParams($params, 'open.mega.player.game.log.url.get');
$result = doCurl($this->apiDomain,$postData);
if (!empty($result['error'])) {
throw new GameException($result['error']['message'], 0);
}
return $result['result'];
}
/**
* 查询全部玩家账单
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getReportRecord(string $startTime = '', string $endTime = '')
{
$params = [
'random' => $this->player->uuid ?? uniqid(),
'sn' => $this->sn,
'agentLoginId' => $this->appId,
'secretCode' => $this->appSecret,
];
$params['digest'] = $this->createSign($params);
$params['startTime'] = $startTime;
$params['endTime'] = $endTime;
$params['timeZone'] = 1;
$params['type'] = 1;
$postData = $this->buildParams($params, 'open.mega.player.total.report');
$result = doCurl($this->apiDomain,$postData);
if (!empty($result['error'])) {
throw new GameException($result['error']['message'], 0);
}
return $result['result'];
}
/**
* 获取电子游戏注单分页列表
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getGameOrderById(string $startTime = '', string $endTime = '')
{
$params = [
'random' => $this->player->uuid ?? uniqid(),
'sn' => $this->sn,
'loginId' => $this->loginId,
'secretCode' => $this->appSecret,
];
$params['digest'] = $this->createSign($params);
$params['startTime'] = $startTime;
$params['endTime'] = $endTime;
$params['timeZone'] = 1;
$postData = $this->buildParams($params, 'open.mega.game.order.page');
$result = doCurl($this->apiDomain,$postData);
if (!empty($result['error'])) {
throw new GameException($result['error']['message'], 0);
}
return $result;
}
/**
* 查询玩家游戏记录
* @return array
*/
public function handleOrderHistories(): array
{
try {
$list = [];
$startTime = date('Y-m-d');
$endTime = date('Y-m-d');
$data = $this->getReportRecord($startTime, $endTime);
if (!empty($data)) {
foreach ($data as $item) {
$list[] = [
'player_code' => $item['loginId'],
'platform_id' => $this->platform->id,
'game_code' => 'MeGa888',
'bet' => $item['bet'],
'win' => $item['win'],
'order_no' => $item['userId'].date('Ymd'),
'original_data' => json_encode($item,JSON_UNESCAPED_UNICODE),
'platform_action_at' => $startTime,
'game_type' => 1,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
];
}
}
} catch (Exception $e) {
return [];
}
return $list;
}
}

View File

@@ -0,0 +1,444 @@
<?php
namespace app\service\game;
use addons\webman\model\Game;
use addons\webman\model\GamePlatform;
use addons\webman\model\Player;
use addons\webman\model\PlayerGamePlatform;
use addons\webman\model\PlayerWalletTransfer;
use app\exception\GameException;
use Exception;
use support\Log;
use support\Response;
class MonkeyKingServiceInterface extends GameServiceFactory implements GameServiceInterface
{
public $method = 'POST';
public $successCode = 'S100';
public $loginId;
public $gameType = [
'10' => 'Slot',
'12' => 'Casino',
'13' => 'Arcade',
'16' => 'Fishing'
];
public $failCode = [
'S200' => '重復請求',
'150001' => '無效的簽名',
'150002' => '無效的SN',
'150003' => '無效的參數',
'150004' => '無效的貨幣',
'150005' => '玩家已存在',
'150006' => '玩家不存在',
'150007' => '次级代理不存在',
'150008' => '執行失敗',
'150009' => '無效的方法',
'150010' => '無效的用戶狀態',
'150011' => '玩家狀態無需更新',
'150012' => '超出數據範圍',
'150013' => '無匹配數據',
'150014' => '登入位置被禁止',
'150015' => '分數不足夠',
'150016' => '不支持禮碼',
'150017' => '交易流水號不得重複',
'150018' => '系統繁忙',
'150019' => '日期時間各式錯誤',
'150020' => '超出時間限制範圍開始時間與結束時間之間不能大於120分鐘',
];
private $apiDomain;
private $domain;
private $appId;
private $appSecret;
private $path = [
'createPlayer' => '/UserInfo/CreatePlayer',
'getPlayer' => '/UserInfo/GetPlayer',
'getGameList' => '/Game/GetGameList',
'getSimpleGameList' => '/Game/GetSimpleGameList',
'getLoginH5' => '/UserInfo/GetLoginH5',
'setPlayerStatus' => '/UserInfo/SetPlayerStatus',
'getBalance' => '/Account/GetBalance',
'setBalanceTransfer' => '/Account/SetBalanceTransfer',
'getGameRecordByTime' => '/Game/GetGameRecordByTime',
'getGameRecord' => '/Game/GetGameRecord',
'removeRecords' => '/Game/RemoveRecords',
];
private $lang = [
'zh-CN' => 'zh-ch',
'en' => 'en_us',
'zh_tc' => 'zh_tc',
'en-us' => 'en-us',
'id' => 'id',
'th' => 'th',
'my' => 'my',
'vi' => 'vi',
'fi_fi' => 'fi_fi',
'kr_ko' => 'kr_ko',
'hi_hi' => 'hi_hi',
'br_po' => 'br_po',
'lo_la' => 'lo_la',
'cam_dia' => 'en_us', // 柬埔寨语
];
/**
* @param Player|null $player
* @param $type
* @throws Exception
*/
public function __construct($type, Player $player = null)
{
$config = config('game_platform.' . $type);
$this->appId = $config['app_id'];
$this->apiDomain = $config['api_domain'];
$this->domain = $config['domain'];
$this->appSecret = $config['app_secret'];
$this->platform = GamePlatform::query()->where('name', $type)->first();
if (!empty($player)) {
$this->player = $player;
$this->getLoginId();
}
}
/**
* @throws Exception
*/
protected function getLoginId()
{
/** @var PlayerGamePlatform $playerGamePlatform */
$playerGamePlatform = $this->player->playerGamePlatform->where('platform_id', $this->platform->id)->first();
if (!empty($playerGamePlatform)) {
return $this->loginId = $playerGamePlatform->player_code;
}
return $this->createPlayer([
'uuid' => $this->player->uuid,
'name' => $this->player->name,
]);
}
/**
* 创建玩家
* @param array $data
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function createPlayer(array $data = [])
{
$params = [
'ID' => createOrderNo(),
'Method' => 'CreatePlayer',
'SN' => $this->appId,
'PlayerCode' => $data['uuid'],
];
$params['Signature'] = $this->createSign($params);
$params['PlayerName'] = $data['uuid'];
Log::info('MonkeyKing请求参数', [$params]);
$res = doCurl($this->createUrl('createPlayer'), $params);
Log::info('MonkeyKing请求返回数据', [$res]);
if ($res['code'] != $this->successCode) {
throw new GameException($this->failCode[$res['code']], 0);
}
$playerGamePlatform = new PlayerGamePlatform();
$playerGamePlatform->player_id = $this->player->id;
$playerGamePlatform->platform_id = $this->platform->id;
$playerGamePlatform->player_name = $data['name'];
$playerGamePlatform->player_code = $data['uuid'];
$playerGamePlatform->save();
$this->loginId = $playerGamePlatform->player_code;
return $res;
}
public function createSign($params): string
{
return md5(implode('', $params) . $this->appSecret);
}
/**
* 生成请求url
* @param $method
* @return string
*/
public function createUrl($method): string
{
return $this->apiDomain . $this->path[$method];
}
/**
* 创建玩家
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getPlayer()
{
$params = [
'ID' => createOrderNo(),
'Method' => 'GetPlayer',
'SN' => $this->appId,
'LoginId' => $this->loginId,
];
$params['Signature'] = $this->createSign($params);
Log::info('MonkeyKing请求参数', [$params]);
$data = doCurl($this->createUrl('getPlayer'), $params);
Log::info('MonkeyKing请求返回数据', [$data]);
if ($data['code'] != $this->successCode) {
throw new GameException($this->failCode[$data['code']], 0);
}
return $data;
}
/**
* 获取游戏摘要MD5 (id+method+sn+APlSecretKey)
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getSimpleGameList()
{
$params = [
'ID' => createOrderNo(),
'Method' => 'GetSimpleGameList',
'SN' => $this->appId,
];
$params['Signature'] = $this->createSign($params);
Log::info('MonkeyKing请求参数', [$params]);
$data = doCurl($this->createUrl('getSimpleGameList'), $params);
Log::info('MonkeyKing请求返回数据', [$data]);
if ($data['code'] != $this->successCode) {
throw new GameException($this->failCode[$data['code']], 0);
}
if (!empty($data['data']['games'])) {
foreach ($data['data']['games'] as $game) {
Game::query()->updateOrCreate(
[
'platform_id' => $this->platform->id,
'game_code' => $game['gameCode'],
'platform_game_type' => $game['type'],
]
);
}
}
return $data;
}
/**
* 获取游戏摘要MD5 (id+method+sn+APlSecretKey)
* @param array $data
* @return string
* @throws GameException|\think\Exception
*/
public function login(array $data = []): string
{
$params = [
'ID' => createOrderNo(),
'Method' => 'GetLoginH5',
'SN' => $this->appId,
'LoginId' => $this->loginId,
];
$params['Signature'] = $this->createSign($params);
Log::info('MonkeyKing请求参数', [$params]);
$res = doCurl($this->createUrl('getLoginH5'), $params);
Log::info('MonkeyKing请求返回数据', [$res]);
if ($res['code'] != $this->successCode) {
throw new GameException($this->failCode[$res['code']], 0);
}
$responseH5 = [
'Language' => $this->lang[$data['lang']] ?? 'ch',
"GameId" => $data['gameCode'],
'CallbackAddress' => $data['callBackUrl'] ?? '',
"AppType" => $data['appType'] ?? 1,
"DeviceType" => 1,
];
$jsonString = json_encode($responseH5);
$parametersValue = base64_encode($jsonString);
return $this->domain . '/linkgame' . ($res['data']['loginUrl'] ?? '') . '&' . $parametersValue . '&' . $data['gameCode'];
}
/**
* 設置登入玩家狀態。當玩家處於禁用狀態,該玩家無法在平台進行任何操作,如果玩家在遊戲進行中該玩家將會自動退出遊戲。
* 簽名密鑰方式 Md5(Id+Method+SN+LoginId+APISecretKey)
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function setPlayerStatus($status)
{
$params = [
'ID' => createOrderNo(),
'Method' => 'GetSimpleGameList',
'SN' => $this->appId,
'LoginId' => $this->loginId,
];
$params['Signature'] = $this->createSign($params);
$params['Status'] = $status;
Log::info('MonkeyKing请求参数', [$params]);
$data = doCurl($this->createUrl('setPlayerStatus'), $params);
Log::info('MonkeyKing请求返回数据', [$data]);
if ($data['code'] != $this->successCode) {
throw new GameException($this->failCode[$data['code']], 0);
}
return $data;
}
/**
* 玩家钱包转入游戏平台
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function balanceTransferOut()
{
return $this->setBalanceTransfer(PlayerWalletTransfer::TYPE_OUT, $this->player->wallet->money);
}
/**
* 轉帳進出額度
* 簽名密鑰方式 MD5 (Id+Method+SN+LoginId+APISecretKey)
* @param $type
* @param float $amount
* @param float $reward
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
protected function setBalanceTransfer($type, float $amount = 0, float $reward = 0)
{
if ($amount == 0 && $reward == 0) {
throw new GameException('转出/入金额错误', 0);
}
$params = [
'ID' => createOrderNo(),
'Method' => 'SetBalanceTransfer',
'SN' => $this->appId,
'LoginId' => $this->loginId,
];
$params['Signature'] = $this->createSign($params);
$params['Amount'] = $amount;
$params['Reward'] = $reward;
Log::info('MonkeyKing请求参数', [$params]);
$res = doCurl($this->createUrl('setBalanceTransfer'), $params);
Log::info('MonkeyKing请求返回数据', [$res]);
if ($res['code'] != $this->successCode) {
Log::error($this->failCode[$res['code']], ['res' => $res]);
throw new GameException($this->failCode[$res['code']], 0);
}
// 记录玩家钱包转出转入记录
$this->createWalletTransfer($type, $amount, $reward, $res['data']['refId'] ?? '');
return $res;
}
/**
* 玩家钱包转入游戏平台
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function balanceTransferIn()
{
$balance = $this->getBalance();
return $this->setBalanceTransfer(PlayerWalletTransfer::TYPE_IN, !empty($balance['data']['result']) ? -$balance['data']['result'] : 0, !empty($balance['data']['reward']) ? -$balance['data']['reward'] : 0);
}
/**
* 獲取玩家餘額信息
* 簽名密鑰方式 MD5 (Id+Method+SN+LoginId+APISecretKey)
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getBalance()
{
$params = [
'ID' => createOrderNo(),
'Method' => 'GetBalance',
'SN' => $this->appId,
'LoginId' => $this->loginId,
];
$params['Signature'] = $this->createSign($params);
Log::info('MonkeyKing请求参数', [$params]);
$data = doCurl($this->createUrl('getBalance'), $params);
Log::info('MonkeyKing请求返回数据', [$data]);
if ($data['code'] != $this->successCode) {
throw new GameException($this->failCode[$data['code']], 0);
}
return $data;
}
/**
* 依據時間獲取遊戲紀錄
* MD5 (Id+Method+SN+StartTime+EndTime+APISecretKey)
* @param int $pageIndex
* @param string $startTime
* @param string $endTime
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function getGameRecordByTime(int $pageIndex = 1, string $startTime = '', string $endTime = '')
{
$params = [
'ID' => createOrderNo(),
'Method' => 'GetGameRecordByTime',
'SN' => $this->appId,
'StartTime' => $startTime,
'EndTime' => $endTime,
];
$pageSize = 500;
$params['Signature'] = $this->createSign($params);
$params['PageSize'] = $pageSize;
$params['PageIndex'] = $pageIndex;
Log::info('MonkeyKing请求参数', [$params]);
$res = doCurl($this->createUrl('getGameRecordByTime'), $params);
Log::info('MonkeyKing请求返回数据', [$res]);
if ($res['code'] != $this->successCode) {
throw new GameException($this->failCode[$res['code']], 0);
}
return $res;
}
/**
* 獲取玩家遊戲歷史紀錄
* MD5 (Id+Method+SN+APISecretKey)
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function getGameRecord()
{
$params = [
'ID' => createOrderNo(),
'Method' => 'GetGameRecord',
'SN' => $this->appId,
];
$params['Signature'] = $this->createSign($params);
Log::info('MonkeyKing请求参数', [$params]);
$res = doCurl($this->createUrl('getGameRecord'), $params);
Log::info('MonkeyKing请求返回数据', [$res]);
if ($res['code'] != $this->successCode) {
throw new GameException($this->failCode[$res['code']], 0);
}
return $res;
}
/**
* 刪除遊戲紀錄
* MD5 (Id+Method+SN+IdsToBeRemoved+APISecretKey)
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function removeRecords($idsToBeRemoved)
{
$params = [
'ID' => createOrderNo(),
'Method' => 'RemoveRecords',
'SN' => $this->appId,
'IdsToBeRemoved' => implode(',', $idsToBeRemoved),
];
$params['Signature'] = $this->createSign($params);
Log::info('MonkeyKing请求参数', [$params]);
$res = doCurl($this->createUrl('removeRecords'), $params);
Log::info('MonkeyKing请求返回数据', [$res]);
if ($res['code'] != $this->successCode) {
throw new GameException($this->failCode[$res['code']], 0);
}
return $res;
}
}

View File

@@ -0,0 +1,504 @@
<?php
namespace app\service\game;
use addons\webman\model\Game;
use addons\webman\model\GamePlatform;
use addons\webman\model\Player;
use addons\webman\model\PlayerGamePlatform;
use addons\webman\model\PlayerWalletTransfer;
use app\exception\GameException;
use Exception;
use support\Log;
use support\Response;
class NextSpinServiceInterface extends GameServiceFactory implements GameServiceInterface
{
public $method = 'POST';
private $apiDomain;
private $domain;
private $appId;
private $appSecret;
private $path = [
'ListGames' => 'ListGames',//获取游戏列表
'createPlayer' => '/UserInfo/CreatePlayer',
'getPlayer' => '/UserInfo/GetPlayer',
'getGameList' => '/Game/GetGameList',
'getSimpleGameList' => '/Game/GetSimpleGameList',
'getLoginH5' => '/UserInfo/GetLoginH5',
'setPlayerStatus' => '/UserInfo/SetPlayerStatus',
'getBalance' => '/Account/GetBalance',
'setBalanceTransfer' => '/Account/SetBalanceTransfer',
'getGameRecordByTime' => '/Game/GetGameRecordByTime',
'getGameRecord' => '/Game/GetGameRecord',
'removeRecords' => '/Game/RemoveRecords',
];
public $successCode = 'S100';
public $loginId;
public $gameType = [
'AD' => 'Casino',
'SM' => 'Fishing',
];
public $localGameType = [
'AD' => '2',
'SM' => '4',
];
public $failCode = [
'S200' => '重復請求',
'F0001' => '無效的簽名',
'F0002' => '無效的SN',
'F0003' => '無效的參數',
'F0004' => '無效的貨幣',
'F0005' => '玩家已存在',
'F0006' => '玩家不存在',
'F0007' => '會員不存在',
'F0008' => '執行失敗',
'F0009' => '無效的方法',
'F0010' => '無效的用戶狀態',
'F0011' => '玩家狀態無需更新',
'F0012' => '超出數據範圍',
'F0013' => '無匹配數據',
'F0014' => '登入位置被禁止',
'F0015' => '分數不足夠',
'F0016' => '不支持禮碼',
'F0017' => '交易流水號不得重複',
'F0018' => '系統繁忙',
'F0019' => '日期時間各式錯誤',
'F0020' => '超出時間限制範圍開始時間與結束時間之間不能大於120分鐘',
'F0021' => '執行取消',
'M0001' => '系統維護',
'M0002' => '系統錯誤',
];
private $lang = [
'zh-CN' => 'zh_ch',
'en' => 'en_us',
'zh_tc' => 'zh_tc',
'th_th' => 'th_th',
'Ma_my' => 'Ma_my',
'vi_nam' => 'vi_nam',
'Fi_fi' => 'Fi_fi',
'Kr_ko' => 'Kr_ko',
'Hi_hi' => 'Hi_hi',
'My_mar' => 'My_mar',
'Br_po' => 'Br_po',
'cam_dia' => 'cam_dia', // 柬埔寨语
];
/**
* @param Player|null $player
* @param $type
* @throws Exception
*/
public function __construct($type, Player $player = null)
{
$config = config('game_platform.' . $type);
$this->appId = $config['app_id'];
$this->apiDomain = $config['api_domain'];
$this->domain = $config['domain'];
$this->appSecret = $config['app_secret'];
$this->platform = GamePlatform::query()->where('name', $type)->first();
if (!empty($player)) {
$this->player = $player;
$this->getLoginId();
}
}
public function httpRequest($url,$api,$method = 'POST',$fields)
{
$fields = json_encode(["merchantCode"=> "ZCH6747",
"serialNo"=>"20240722224255982841"]);
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_POSTFIELDS => $fields,
CURLOPT_HTTPHEADER => [
"API: $api",
"Accept: */*",
"Accept-Encoding: gzip, deflate, br",
"Connection: keep-alive",
"Content-Type: application/json",
"DataType: JSON",
"User-Agent: PostmanRuntime-ApipostRuntime/1.1.0"
],
]);
$response = curl_exec($curl);
curl_close($curl);
$data = json_decode($response,true);
return $data;
}
/**
* 更新游戏列表
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getSimpleGameList()
{
$api = 'getGames';
$method = 'POST';
$serialNo = createOrderNo();
$fields = json_encode([
"merchantCode" => $this->appId,
"serialNo" => $serialNo
]);
$result = $this->httpRequest($this->apiDomain,$api,$method,$fields);
if (isset($result['games']) && $result['code'] == 0) {
foreach ($result['games'] as $game) {
Game::query()->updateOrCreate(
[
'platform_id' => $this->platform->id,
'game_code' => $game['gameCode'],
],
[
'platform_game_type' => $game['category'],
'game_type' => $this->localGameType[$game['category']],
'name' => $game['gameName'],
]
);
}
}else{
throw new GameException($result['msg'], 0);
}
return $result;
}
/**
* 获取游戏列表
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getGamesList()
{
$api = 'getGames';
$method = 'POST';
$serialNo = createOrderNo();
$fields = json_encode([
"merchantCode" => $this->appId,
"serialNo" => $serialNo
]);
$result = $this->httpRequest($this->apiDomain,$api,$method,$fields);
if (isset($result['games']) && $result['code'] == 0) {
return $result['games'];
}else{
throw new GameException($result['msg'], 0);
}
}
/**
* 玩游戏
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function playGame()
{
$params = [
'Method' => 'PLAY',
'Username' => $this->player->name,
'RequestID' => createOrderNo(),
'Amount' => $amount,
'Timestamp' => time(),
];
$signature = $this->createSign($params);
$result = doCurl($this->createUrl($signature), $params);
if (isset($result['Token'])) {
return $forwardUrl = $this->domain."?token=".$result['Token']."&game=1jeqx59c7ztqg&mobile=false";
}else{
throw new GameException($result['Message'], 0);
}
}
/**
* @throws Exception
*/
protected function getLoginId()
{
/** @var PlayerGamePlatform $playerGamePlatform */
$playerGamePlatform = $this->player->playerGamePlatform->where('platform_id', $this->platform->id)->first();
if (!empty($playerGamePlatform)) {
return $this->loginId = $playerGamePlatform->player_code;
}
return $this->createPlayer([
'uuid' => $this->player->uuid,
'name' => $this->player->name,
]);
}
/**
* 创建玩家
* @param array $data
* @return array|mixed|Response
* @throws GameException|Exception
*/
public function createPlayer(array $data = [])
{
$params = [
'ID' => createOrderNo(),
'Method' => 'CreatePlayer',
'SN' => $this->appId,
'PlayerCode' => $data['uuid'],
];
$params['Signature'] = $this->createSign($params);
$params['PlayerName'] = $data['uuid'];
$res = doCurl($this->createUrl('createPlayer'), $params);
if ($res['code'] != $this->successCode) {
throw new GameException($this->failCode[$res['code']], 0);
}
$playerGamePlatform = new PlayerGamePlatform();
$playerGamePlatform->player_id = $this->player->id;
$playerGamePlatform->platform_id = $this->platform->id;
$playerGamePlatform->player_name = $data['name'];
$playerGamePlatform->player_code = $data['uuid'];
$playerGamePlatform->save();
$this->loginId = $playerGamePlatform->player_code;
return $res;
}
/**
* 创建玩家
* @return array|mixed|Response
* @throws GameException|Exception
*/
public function getPlayer()
{
$params = [
'ID' => createOrderNo(),
'Method' => 'GetPlayer',
'SN' => $this->appId,
'LoginId' => $this->loginId,
];
$params['Signature'] = $this->createSign($params);
$data = doCurl($this->createUrl('getPlayer'), $params);
if ($data['code'] != $this->successCode) {
throw new GameException($this->failCode[$data['code']], 0);
}
return $data;
}
/**
* 获取游戏摘要MD5 (id+method+sn+APlSecretKey)
* @param array $data
* @return string
* @throws GameException|\think\Exception
*/
public function login(array $data = []): string
{
$params = [
'ID' => createOrderNo(),
'Method' => 'GetLoginH5',
'SN' => $this->appId,
'LoginId' => $this->loginId,
];
$params['Signature'] = $this->createSign($params);
$res = doCurl($this->createUrl('getLoginH5'), $params);
if ($res['code'] != $this->successCode) {
Log::error($this->failCode[$res['code']], ['res' => $res]);
throw new GameException($this->failCode[$res['code']], 0);
}
$responseH5 = [
'Language' => $this->lang[$data['lang']] ?? 'ch',
"GameId" => $data['gameCode'],
'CallbackAddress' => $data['callBackUrl'] ?? '',
"AppType" => $data['appType'] ?? 1,
"DeviceType" => 1,
];
$jsonString = json_encode($responseH5);
$parametersValue = base64_encode($jsonString);
$link = $this->domain . '/linkgame' . ($res['data']['loginUrl'] ?? '') . '&' . $parametersValue;
if (!empty($data['gameCode'])) {
$link .= '&' . $data['gameCode'];
}
return $link;
}
/**
* 設置登入玩家狀態。當玩家處於禁用狀態,該玩家無法在平台進行任何操作,如果玩家在遊戲進行中該玩家將會自動退出遊戲。
* 簽名密鑰方式 Md5(Id+Method+SN+LoginId+APISecretKey)
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function setPlayerStatus($status)
{
$params = [
'ID' => createOrderNo(),
'Method' => 'GetSimpleGameList',
'SN' => $this->appId,
'LoginId' => $this->loginId,
];
$params['Signature'] = $this->createSign($params);
$params['Status'] = $status;
$data = doCurl($this->createUrl('setPlayerStatus'), $params);
if ($data['code'] != $this->successCode) {
throw new GameException($this->failCode[$data['code']], 0);
}
return $data;
}
/**
* 獲取玩家餘額信息
* 簽名密鑰方式 MD5 (Id+Method+SN+LoginId+APISecretKey)
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getBalance()
{
$params = [
'ID' => createOrderNo(),
'Method' => 'GetBalance',
'SN' => $this->appId,
'LoginId' => $this->loginId,
];
$params['Signature'] = $this->createSign($params);
$data = doCurl($this->createUrl('getBalance'), $params);
if ($data['code'] != $this->successCode) {
throw new GameException($this->failCode[$data['code']], 0);
}
return $data;
}
/**
* 玩家钱包转入游戏平台
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function balanceTransferOut()
{
return $this->setBalanceTransfer(PlayerWalletTransfer::TYPE_OUT, $this->player->wallet->money);
}
/**
* 玩家钱包转入游戏平台
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function balanceTransferIn()
{
$balance = $this->getBalance();
return $this->setBalanceTransfer(PlayerWalletTransfer::TYPE_IN, !empty($balance['data']['result']) ? -$balance['data']['result'] : 0, !empty($balance['data']['reward']) ? -$balance['data']['reward'] : 0);
}
/**
* 轉帳進出額度
* 簽名密鑰方式 MD5 (Id+Method+SN+LoginId+APISecretKey)
* @param $type
* @param float $amount
* @param float $reward
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
protected function setBalanceTransfer($type, float $amount = 0, float $reward = 0)
{
if ($amount == 0 && $reward == 0) {
throw new GameException('转出/入金额错误', 0);
}
$params = [
'ID' => createOrderNo(),
'Method' => 'SetBalanceTransfer',
'SN' => $this->appId,
'LoginId' => $this->loginId,
];
$params['Signature'] = $this->createSign($params);
$params['Amount'] = $amount;
$params['Reward'] = $reward;
$res = doCurl($this->createUrl('setBalanceTransfer'), $params);
if ($res['code'] != $this->successCode) {
throw new GameException($this->failCode[$res['code']], 0);
}
// 记录玩家钱包转出转入记录
$this->createWalletTransfer($type, $amount, $reward, $res['data']['refId'] ?? '');
return $res;
}
/**
* 依據時間獲取遊戲紀錄
* MD5 (Id+Method+SN+StartTime+EndTime+APISecretKey)
* @param int $pageIndex
* @param string $startTime
* @param string $endTime
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function getGameRecordByTime(int $pageIndex = 1, string $startTime = '', string $endTime = '')
{
$params = [
'ID' => createOrderNo(),
'Method' => 'GetGameRecordByTime',
'SN' => $this->appId,
'StartTime' => $startTime,
'EndTime' => $endTime,
];
$pageSize = 500;
$params['Signature'] = $this->createSign($params);
$params['PageSize'] = $pageSize;
$params['PageIndex'] = $pageIndex;
$res = doCurl($this->createUrl('getGameRecordByTime'), $params);
if ($res['code'] != $this->successCode) {
throw new GameException($this->failCode[$res['code']], 0);
}
return $res;
}
/**
* 獲取玩家遊戲歷史紀錄
* MD5 (Id+Method+SN+APISecretKey)
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function getGameRecord()
{
$params = [
'ID' => createOrderNo(),
'Method' => 'GetGameRecord',
'SN' => $this->appId,
];
$params['Signature'] = $this->createSign($params);
$res = doCurl($this->createUrl('getGameRecord'), $params);
if ($res['code'] != $this->successCode) {
throw new GameException($this->failCode[$res['code']], 0);
}
return $res;
}
/**
* 刪除遊戲紀錄
* MD5 (Id+Method+SN+IdsToBeRemoved+APISecretKey)
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function removeRecords($idsToBeRemoved)
{
$params = [
'ID' => createOrderNo(),
'Method' => 'RemoveRecords',
'SN' => $this->appId,
'IdsToBeRemoved' => implode(',', $idsToBeRemoved),
];
$params['Signature'] = $this->createSign($params);
$res = doCurl($this->createUrl('removeRecords'), $params);
if ($res['code'] != $this->successCode) {
throw new GameException($this->failCode[$res['code']], 0);
}
return $res;
}
}

View File

@@ -0,0 +1,473 @@
<?php
namespace app\service\game;
use addons\webman\model\Game;
use addons\webman\model\GamePlatform;
use addons\webman\model\Player;
use addons\webman\model\PlayerGamePlatform;
use addons\webman\model\PlayerWalletTransfer;
use app\exception\GameException;
use DateTime;
use DateTimeZone;
use Exception;
use support\Log;
use support\Response;
use WebmanTech\LaravelHttpClient\Facades\Http;
class PragmaticServiceInterface extends GameServiceFactory implements GameServiceInterface
{
public $method = 'POST';
private $apiDomain;
private $domain;
private $appId;
private $appSecret;
private $secureLogin;
private $providerId;
public $loginId;
public $gameType = [
'2' => 'Casino',
'5' => 'Fishing',
'8' => 'Bingo',
'1' => 'Slot',
];
public $localGameType = [
'2' => '2',//赌场
'5' => '4',//捕鱼
'8' => '8',//宾果
'vs' => '1',//斯洛
'lg' => '5',//真人游戏
];
public $failCode = [
'S200' => '重復請求',
'F0001' => '無效的簽名',
'F0002' => '無效的SN',
'F0003' => '無效的參數',
'F0004' => '無效的貨幣',
'F0005' => '玩家已存在',
'F0006' => '玩家不存在',
'F0007' => '會員不存在',
'F0008' => '執行失敗',
'F0009' => '無效的方法',
'F0010' => '無效的用戶狀態',
'F0011' => '玩家狀態無需更新',
'F0012' => '超出數據範圍',
'F0013' => '無匹配數據',
'F0014' => '登入位置被禁止',
'F0015' => '分數不足夠',
'F0016' => '不支持禮碼',
'F0017' => '交易流水號不得重複',
'F0018' => '系統繁忙',
'F0019' => '日期時間各式錯誤',
'F0020' => '超出時間限制範圍開始時間與結束時間之間不能大於120分鐘',
'F0021' => '執行取消',
'M0001' => '系統維護',
'M0002' => '系統錯誤',
];
/**
* @param Player|null $player
* @param $type
* @throws Exception
*/
public function __construct($type, Player $player = null)
{
$config = config('game_platform.' . $type);
$this->apiDomain = $config['api_domain'];
$this->domain = $config['domain'];
$this->appId = $config['name'];
$this->secureLogin = $config['secure_login'];
$this->providerId = $config['provider_id'];
$this->appSecret = $config['app_secret'];
$this->platform = GamePlatform::query()->where('name', $type)->first();
if (!empty($player)) {
$this->player = $player;
$this->getLoginId();
}
}
/**
* 生成请求url
* @param $method
* @return string
*/
public function createUrl($method): string
{
return $this->apiDomain.$method;
}
public function createSign($params): string
{
ksort($params);
return md5(http_build_query($params, '', '&') . $this->appSecret);
}
/**
* 更新游戏列表
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getSimpleGameList()
{
$params = [
'secureLogin' => $this->secureLogin,
];
$signature = $this->createSign($params);
$params['hash'] = $signature;
$result = doFormCurl($this->createUrl('/IntegrationService/v3/http/CasinoGameAPI/getCasinoGames/'), $params);
if ($result['error'] == 0 && !empty($result['gameList'])) {
foreach ($result['gameList'] as $game) {
if ($game['gameTypeID'] != 'vs') {
continue;
}
Game::query()->updateOrCreate(
[
'platform_id' => $this->platform->id,
'game_code' => $game['gameID'],
],
[
'platform_game_type' => $game['gameTypeID'],
'game_type' => $this->localGameType[$game['gameTypeID']],
'name' => $game['gameName'],
// 'game_image' => $this->apiDomain.'/gs2c/common/lobby/v1/apps/slots-lobby-assets/'.$game['gameID'].'/'.$game['gameID'].'_325x234_NB.png'
]
);
}
}else{
throw new GameException($result['description'], 0);
}
return $result;
}
/**
* 获取游戏列表
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getGamesList()
{
$params = [
'secureLogin' => $this->secureLogin,
];
$signature = $this->createSign($params);
$params['hash'] = $signature;
$result = doFormCurl($this->createUrl('/IntegrationService/v3/http/CasinoGameAPI/getCasinoGames/'), $params);
if ($result['error'] == 0 && !empty($result['gameList'])) {
return $result;
}else{
throw new GameException($result['description'], 0);
}
}
/**
* 获取玩家ID
* @throws Exception
*/
protected function getLoginId()
{
/** @var PlayerGamePlatform $playerGamePlatform */
$playerGamePlatform = PlayerGamePlatform::query()->where('platform_id', $this->platform->id)->where('player_id',$this->player->id)->first();
if (!empty($playerGamePlatform)) {
return $this->loginId = $playerGamePlatform->player_code;
}
return $this->createPlayer([
'uuid' => $this->player->uuid,
'name' => $this->player->name,
]);
}
/**
* 创建玩家
* @param array $data
* @return array|mixed|Response
* @throws GameException|Exception
*/
public function createPlayer(array $data = [])
{
$params = [
'secureLogin' => $this->secureLogin,
'externalPlayerId' => $data['uuid'],
'currency' => 'MYR',
];
$signature = $this->createSign($params);
$params['hash'] = $signature;
$result = doFormCurl($this->createUrl('/IntegrationService/v3/http/CasinoGameAPI/player/account/create/'), $params);
if ($result['error'] != 0 || empty($result['playerId'])) {
Log::error($result['description'], ['res' => $result]);
throw new GameException($result['description'], 0);
}
$playerGamePlatform = new PlayerGamePlatform();
$playerGamePlatform->player_id = $this->player->id;
$playerGamePlatform->platform_id = $this->platform->id;
$playerGamePlatform->player_name = $data['name'];
$playerGamePlatform->player_code = $data['uuid'];
$playerGamePlatform->save();
$this->loginId = $playerGamePlatform->player_code;
return $result;
}
/**
* 获取玩家
* @return false
* @throws Exception
*/
public function getPlayer(): bool
{
return false;
}
/**
* 玩家进入游戏
* @param array $data
* @return string
* @throws GameException|\think\Exception
*/
public function login(array $data = []): string
{
$params = [
'secureLogin' => $this->secureLogin,
'externalPlayerId' => $this->loginId,
'gameId' => $data['gameCode'],
'language' => 'en',
];
$signature = $this->createSign($params);
$params['hash'] = $signature;
$result = doFormCurl($this->createUrl('/IntegrationService/v3/http/CasinoGameAPI/game/start/'), $params);
if ($result['error'] == 0 && !empty($result['gameURL'])) {
$link = $result['gameURL'];
}else{
throw new GameException($result['description'], 0);
}
return $link;
}
/**
* 获取玩家游戏平台余额
* @return array|mixed|Response
* @throws GameException|\think\Exception
*/
public function getBalance()
{
$params = [
'secureLogin' => $this->secureLogin,
'externalPlayerId' => $this->loginId,
];
$signature = $this->createSign($params);
$params['hash'] = $signature;
$result = doFormCurl($this->createUrl('/IntegrationService/v3/http/CasinoGameAPI/balance/current/'), $params);
if ($result['error'] != 0) {
throw new GameException('Pragmatic System Error,Please contact the administrator', 0);
}
return $result['balance'];
}
/**
* 玩家钱包转入游戏平台
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function balanceTransferOut()
{
return $this->setBalanceTransfer(PlayerWalletTransfer::TYPE_OUT, $this->player->wallet->money);
}
/**
* 游戏平台转入玩家钱包
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
public function balanceTransferIn()
{
$balance = $this->getBalance();
if($balance == 0){
// 记录玩家钱包转出转入记录
$this->createWalletTransfer(PlayerWalletTransfer::TYPE_IN, 0, 0);
return true;
}
return $this->setBalanceTransfer(PlayerWalletTransfer::TYPE_IN, $balance ? -$balance : 0);
}
/**
* 轉帳進出額度
* @param $type
* @param float $amount
* @param float $reward
* @return array|mixed|null
* @throws GameException|\think\Exception
*/
protected function setBalanceTransfer($type, float $amount = 0, float $reward = 0)
{
$params = [
'secureLogin' => $this->secureLogin,
'externalPlayerId' => $this->loginId,
'externalTransactionId' => createOrderNo(),
'amount' => $amount,
];
$signature = $this->createSign($params);
$params['hash'] = $signature;
$result = doFormCurl($this->createUrl('/IntegrationService/v3/http/CasinoGameAPI/balance/transfer/'), $params);
if ($result['error'] != 0) {
throw new GameException($result['description'], 0);
}
// 记录玩家钱包转出转入记录
$this->createWalletTransfer($type, $amount, $reward, $result['transactionId'] ?? '');
return $result;
}
/**
* 转账记录
* @param $startTime
* @return array|mixed|null
* @throws GameException
* @throws \think\Exception
*/
protected function transferTransactions($startTime)
{
$params = [
'secureLogin' => $this->secureLogin,
//'timepoint' => $startTime ?? round(microtime(true) * 1000),
];
$signature = $this->createSign($params);
$params['hash'] = $signature;
$result = doFormCurl($this->createUrl('/IntegrationService/v3/http/CasinoGameAPI/balance/transfer/transactions/'), $params);
if ($result['error'] != 0) {
throw new GameException($result['description'], 0);
}
return $result;
}
/**
* 踢出游戏
* @return array|mixed|Response
* @throws GameException|Exception
*/
public function terminateSession()
{
$params = [
'secureLogin' => $this->secureLogin,
'externalPlayerId' => $this->loginId,
];
$signature = $this->createSign($params);
$params['hash'] = $signature;
$result = doFormCurl($this->createUrl('/IntegrationService/v3/http/CasinoGameAPI/game/session/terminate'), $params);
if ($result['error'] == 0) {
throw new GameException($result['description'], 0);
}
return $result;
}
/**
* 重播链接
* @return array|mixed|Response
* @throws GameException|Exception
*/
public function replayLink($roundId)
{
$params = [
'secureLogin' => $this->secureLogin,
'externalPlayerId' => $this->loginId,
'roundId' => $roundId,
];
$signature = $this->createSign($params);
$params['hash'] = $signature;
$result = doFormCurl($this->createUrl('/IntegrationService/v3/http/ReplayAPI/getSharedLink'), $params);
if ($result['error'] == 0) {
throw new GameException($result['description'], 0);
}
return $result;
}
/**
* 查询游戏纪录
* @return array
*/
public function getGameRecordList(): array
{
$params = [
'login' => $this->secureLogin,
'password' => $this->appSecret,
'timepoint' => round(microtime(true) * 1000) - 90000,
];
$query = http_build_query($params, '', '&');
$url = $this->createUrl('/IntegrationService/v3/DataFeeds/gamerounds/finished/?' . $query);
$response = Http::timeout(10)->get($url);
$result = $response->body();
return $this->parseCustomCsv($result);
}
/**
* CSV转数组
* @param $input
* @return array
*/
public function parseCustomCsv($input): array
{
$lines = explode("\n", trim($input)); // 分割为行数组
// 解析timepoint
$timepoint = (int) substr($lines[0], strpos($lines[0], '=') + 1);
array_shift($lines); // 移除timepoint行
// 处理CSV部分
$header = str_getcsv(array_shift($lines)); // 获取标题行
$result = [
'timepoint' => $timepoint,
'data' => []
];
foreach ($lines as $line) {
$row = str_getcsv($line);
if (count($row) !== count($header)) continue; // 跳过列数不匹配的行
// 组合关联数组并转换数据类型
$entry = array_combine($header, array_map(function($value) {
if ($value === 'null') return null; // 转换null字符串
if (is_numeric($value)) { // 转换数字类型
return (strpos($value, '.') !== false) ? (float)$value : (int)$value;
}
return $value;
}, $row));
$result['data'][] = $entry;
}
return $result;
}
/**
* 查询玩家游戏记录
* @return array
*/
public function handleOrderHistories(): array
{
try {
$list = [];
$data = $this->getGameRecordList();
if (!empty($data['data'])) {
foreach ($data['data'] as $item) {
$list[] = [
'uuid' => $item['extPlayerID'],
'platform_id' => $this->platform->id,
'game_code' => $item['gameID'],
'bet' => $item['bet'],
'win' => $item['win'],
'order_no' => $item['playSessionID'],
'original_data' => json_encode($item,JSON_UNESCAPED_UNICODE),
'platform_action_at' => date('Y-m-d H:i:s', strtotime($item['endDate'])),
'game_type' => 'vs',
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
];
}
}
} catch (Exception $e) {
return [];
}
return $list;
}
}