1.修改电话号码格式为60前缀,马来西亚格式
2.优化渠道可以查看分红方式,可以查看游玩详情
This commit is contained in:
@@ -6,6 +6,7 @@ namespace app\common\controller;
|
||||
|
||||
use Throwable;
|
||||
use app\admin\library\Auth;
|
||||
use app\common\service\AdminChannelScopeService;
|
||||
use app\common\library\token\TokenExpirationException;
|
||||
use app\admin\library\traits\Backend as BackendTrait;
|
||||
use support\Response;
|
||||
@@ -421,7 +422,7 @@ class Backend extends Api
|
||||
*/
|
||||
protected function getDataLimitAdminIds(): array
|
||||
{
|
||||
if (!$this->dataLimit || !$this->auth || $this->auth->isSuperAdmin()) {
|
||||
if (!$this->dataLimit || !$this->auth || $this->auth->isSuperAdmin() || $this->hasGlobalReadScope()) {
|
||||
return [];
|
||||
}
|
||||
$adminIds = [];
|
||||
@@ -475,6 +476,51 @@ class Backend extends Api
|
||||
return get_controller_path($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 全平台只读范围:角色组均未绑定渠道,或拥有查看所有渠道权限,或超管
|
||||
*/
|
||||
protected function hasGlobalReadScope(): bool
|
||||
{
|
||||
return $this->auth !== null && AdminChannelScopeService::hasGlobalReadScope($this->auth);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否按「名下管理员/用户」收窄列表(非超管且非全平台只读范围)
|
||||
*/
|
||||
protected function shouldApplyUserAdminScope(): bool
|
||||
{
|
||||
if ($this->auth === null || !$this->auth->isLogin()) {
|
||||
return false;
|
||||
}
|
||||
if ($this->auth->isSuperAdmin() || $this->hasGlobalReadScope()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 可读渠道 ID;null 表示全部渠道
|
||||
*
|
||||
* @return array<int, int>|null
|
||||
*/
|
||||
protected function readableChannelIds(): ?array
|
||||
{
|
||||
if ($this->auth === null) {
|
||||
return [0];
|
||||
}
|
||||
|
||||
return AdminChannelScopeService::readableChannelIds($this->auth);
|
||||
}
|
||||
|
||||
/**
|
||||
* 列表是否需要按 channel_id 收窄
|
||||
*/
|
||||
protected function shouldApplyChannelIdScope(): bool
|
||||
{
|
||||
return $this->readableChannelIds() !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造权限节点候选:兼容 snake_case 与 camelCase 节点名
|
||||
*
|
||||
|
||||
@@ -120,6 +120,11 @@ return [
|
||||
'createNextManual' => 'Create next period manually',
|
||||
'periodSettings' => 'Period settings',
|
||||
'manualSettle' => 'Manual settle',
|
||||
'batchSettlePending' => 'Batch settle pending channels',
|
||||
'viewDividendRecords' => 'View paid dividend records',
|
||||
'viewDirectBetRecords' => 'View direct bet records',
|
||||
'viewSettlementBetRecords' => 'View settlement-scope bets',
|
||||
'viewAllChannels' => 'View all channels',
|
||||
|
||||
// 其它中文按钮文案
|
||||
'期号开关' => 'Period toggle',
|
||||
|
||||
@@ -115,6 +115,7 @@ return [
|
||||
'Only super admin can settle; after settlement, commissions will be automatically paid to admin wallets' => 'Only super admin can settle; after settlement, commissions will be automatically paid to admin wallets',
|
||||
'Settlement failed' => 'Settlement failed',
|
||||
'Super admin settlement completed; paid automatically by share ratios' => 'Super admin settlement completed; paid automatically by share ratios',
|
||||
'Settlement completed; commissions paid automatically by share ratios' => 'Settlement completed; commissions paid automatically by share ratios',
|
||||
'Batch settlement completed' => 'Batch settlement completed',
|
||||
|
||||
'Invalid settlement cycle' => 'Invalid settlement cycle',
|
||||
|
||||
@@ -53,6 +53,10 @@ return [
|
||||
'periodSettings' => '期号设置',
|
||||
'manualSettle' => '手动结算',
|
||||
'batchSettlePending' => '批量结算待结算渠道',
|
||||
'viewDividendRecords' => '查看已分红记录',
|
||||
'viewDirectBetRecords' => '查看直属投注记录',
|
||||
'viewSettlementBetRecords' => '查看总投注金额',
|
||||
'viewAllChannels' => '查看所有渠道',
|
||||
'walletAdjust' => '钱包加减点',
|
||||
'Markdown文档' => 'Markdown文档',
|
||||
'分红说明文档' => '分红说明文档',
|
||||
|
||||
@@ -115,6 +115,7 @@ return [
|
||||
'Only super admin can settle; after settlement, commissions will be automatically paid to admin wallets' => '仅超管可执行结算,结算后系统会自动发放至管理员钱包',
|
||||
'Settlement failed' => '结算失败',
|
||||
'Super admin settlement completed; paid automatically by share ratios' => '超管结算完成,已按分配比例自动发放给管理员',
|
||||
'Settlement completed; commissions paid automatically by share ratios' => '结算完成,已按分配比例自动发放给管理员',
|
||||
'Batch settlement completed' => '批量结算完成',
|
||||
|
||||
'Invalid settlement cycle' => '结算周期不合法',
|
||||
|
||||
@@ -105,7 +105,7 @@ class Auth extends \ba\Auth
|
||||
$this->setError(__('Email'));
|
||||
return false;
|
||||
}
|
||||
$isMobileUsername = preg_match('/^1[3-9]\d{9}$/', $username) === 1;
|
||||
$isMobileUsername = MalaysiaMobilePhone::isValid($username);
|
||||
$isNormalUsername = preg_match('/^[a-zA-Z][a-zA-Z0-9_]{2,15}$/', $username) === 1;
|
||||
if ($username && !$isMobileUsername && !$isNormalUsername) {
|
||||
$this->setError(__('Username'));
|
||||
@@ -127,7 +127,7 @@ class Auth extends \ba\Auth
|
||||
return false;
|
||||
}
|
||||
|
||||
$nickname = preg_replace_callback('/1[3-9]\d{9}/', fn($m) => substr($m[0], 0, 3) . '****' . substr($m[0], 7), $username);
|
||||
$nickname = MalaysiaMobilePhone::maskInNickname($username);
|
||||
$time = time();
|
||||
$data = [
|
||||
'group_id' => $group,
|
||||
@@ -170,9 +170,13 @@ class Auth extends \ba\Auth
|
||||
public function login(string $username, string $password, bool $keep): bool
|
||||
{
|
||||
$accountType = false;
|
||||
if (preg_match('/^1[3-9]\d{9}$/', $username)) $accountType = 'phone';
|
||||
elseif (filter_var($username, FILTER_VALIDATE_EMAIL)) $accountType = 'email';
|
||||
elseif (preg_match('/^[a-zA-Z][a-zA-Z0-9_]{2,15}$/', $username)) $accountType = 'username';
|
||||
if (MalaysiaMobilePhone::isValid($username)) {
|
||||
$accountType = 'phone';
|
||||
} elseif (filter_var($username, FILTER_VALIDATE_EMAIL)) {
|
||||
$accountType = 'email';
|
||||
} elseif (preg_match('/^[a-zA-Z][a-zA-Z0-9_]{2,15}$/', $username)) {
|
||||
$accountType = 'username';
|
||||
}
|
||||
if (!$accountType) {
|
||||
$this->setError('Account not exist');
|
||||
return false;
|
||||
|
||||
29
app/common/library/MalaysiaMobilePhone.php
Normal file
29
app/common/library/MalaysiaMobilePhone.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\common\library;
|
||||
|
||||
/**
|
||||
* 马来西亚手机号(60 国际前缀,不含 +)
|
||||
*/
|
||||
class MalaysiaMobilePhone
|
||||
{
|
||||
public const PATTERN = '/^60(1[0-9])\d{7,9}$/';
|
||||
|
||||
public static function isValid(string $phone): bool
|
||||
{
|
||||
return $phone !== '' && preg_match(self::PATTERN, $phone) === 1;
|
||||
}
|
||||
|
||||
public static function maskInNickname(string $text): string
|
||||
{
|
||||
$masked = preg_replace_callback(
|
||||
self::PATTERN,
|
||||
static fn(array $matches): string => substr($matches[0], 0, 3) . '****' . substr($matches[0], -4),
|
||||
$text
|
||||
);
|
||||
|
||||
return is_string($masked) ? $masked : $text;
|
||||
}
|
||||
}
|
||||
123
app/common/service/AdminChannelScopeService.php
Normal file
123
app/common/service/AdminChannelScopeService.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\common\service;
|
||||
|
||||
use app\admin\library\Auth;
|
||||
use support\think\Db;
|
||||
|
||||
/**
|
||||
* 后台管理员渠道数据范围:角色组 channel_id 未绑定时可读全平台;否则按绑定渠道过滤。
|
||||
*/
|
||||
class AdminChannelScopeService
|
||||
{
|
||||
/**
|
||||
* 是否具备全平台只读范围(超管 / 角色组均未绑定渠道 / 拥有查看所有渠道权限)
|
||||
*/
|
||||
public static function hasGlobalReadScope(Auth $auth): bool
|
||||
{
|
||||
if (!$auth->isLogin()) {
|
||||
return false;
|
||||
}
|
||||
if ($auth->isSuperAdmin()) {
|
||||
return true;
|
||||
}
|
||||
if (self::resolveBoundGroupChannelIds($auth) === []) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return self::hasViewAllChannelsPermission($auth);
|
||||
}
|
||||
|
||||
/**
|
||||
* 可读渠道 ID 列表;null 表示不限制(全部渠道)
|
||||
*
|
||||
* @return array<int, int>|null
|
||||
*/
|
||||
public static function readableChannelIds(Auth $auth): ?array
|
||||
{
|
||||
if (!$auth->isLogin()) {
|
||||
return [0];
|
||||
}
|
||||
if (self::hasGlobalReadScope($auth)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$ids = self::resolveBoundGroupChannelIds($auth);
|
||||
if ($ids !== []) {
|
||||
return $ids;
|
||||
}
|
||||
|
||||
$selfChannelId = (int) Db::name('admin')->where('id', (int) $auth->id)->value('channel_id');
|
||||
if ($selfChannelId > 0) {
|
||||
return [$selfChannelId];
|
||||
}
|
||||
|
||||
return [0];
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前管理员所属角色组上绑定的渠道 ID(去重)
|
||||
*
|
||||
* @return array<int, int>
|
||||
*/
|
||||
public static function resolveBoundGroupChannelIds(Auth $auth): array
|
||||
{
|
||||
$uid = (int) $auth->id;
|
||||
if ($uid <= 0) {
|
||||
return [];
|
||||
}
|
||||
$groupIds = Db::name('admin_group_access')->where('uid', $uid)->column('group_id');
|
||||
if ($groupIds === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$rows = Db::name('admin_group')
|
||||
->where('id', 'in', $groupIds)
|
||||
->whereNotNull('channel_id')
|
||||
->where('channel_id', '>', 0)
|
||||
->column('channel_id');
|
||||
|
||||
$ids = [];
|
||||
foreach ($rows as $cid) {
|
||||
$ids[] = (int) $cid;
|
||||
}
|
||||
|
||||
return array_values(array_unique($ids));
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否拥有「查看所有渠道」按钮权限
|
||||
*/
|
||||
public static function hasViewAllChannelsPermission(Auth $auth): bool
|
||||
{
|
||||
if (!$auth->isLogin()) {
|
||||
return false;
|
||||
}
|
||||
$nodes = ['channel/viewAllChannels', 'channel/viewallchannels'];
|
||||
foreach ($nodes as $node) {
|
||||
if ($auth->check($node)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
$ruleList = $auth->getRuleList();
|
||||
foreach ($nodes as $node) {
|
||||
if (in_array(strtolower($node), $ruleList, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 列表按 channel_id 过滤时使用的 ID;null=不过滤
|
||||
*
|
||||
* @return array<int, int>|null
|
||||
*/
|
||||
public static function channelIdFilterForQuery(Auth $auth): ?array
|
||||
{
|
||||
return self::readableChannelIds($auth);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user