[游戏管理]用户管理
This commit is contained in:
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace app\admin\controller\auth;
|
||||
|
||||
use ba\Random;
|
||||
use Throwable;
|
||||
use support\think\Db;
|
||||
use support\validation\Validator;
|
||||
@@ -21,7 +22,7 @@ class Admin extends Backend
|
||||
|
||||
protected array|string $quickSearchField = ['username', 'nickname'];
|
||||
|
||||
protected string|int|bool $dataLimit = 'allAuthAndOthers';
|
||||
protected string|int|bool $dataLimit = 'parent';
|
||||
|
||||
protected string $dataLimitField = 'id';
|
||||
|
||||
@@ -61,9 +62,18 @@ class Admin extends Backend
|
||||
$res = $query
|
||||
->order($order)
|
||||
->paginate($limit);
|
||||
$items = $res->items();
|
||||
$topGroupUids = $this->getTopGroupUserMap(array_column($items, 'id'));
|
||||
foreach ($items as &$item) {
|
||||
$id = $item['id'] ?? null;
|
||||
if ($id === 1 || isset($topGroupUids[$id])) {
|
||||
$item['commission_rate'] = null;
|
||||
}
|
||||
}
|
||||
unset($item);
|
||||
|
||||
return $this->success('', [
|
||||
'list' => $res->items(),
|
||||
'list' => $items,
|
||||
'total' => $res->total(),
|
||||
'remark' => get_route_remark(),
|
||||
]);
|
||||
@@ -149,6 +159,10 @@ class Admin extends Backend
|
||||
if (!$data) {
|
||||
return $this->error(__('Parameter %s can not be empty', ['']));
|
||||
}
|
||||
$data = $this->normalizeSingleGroup($data);
|
||||
if (!$this->hasSingleGroup($data['group_arr'] ?? null)) {
|
||||
return $this->error('请选择且仅选择一个角色组');
|
||||
}
|
||||
|
||||
if ($this->modelValidate) {
|
||||
try {
|
||||
@@ -172,6 +186,25 @@ class Admin extends Backend
|
||||
|
||||
$passwd = $data['password'] ?? '';
|
||||
$data = $this->excludeFields($data);
|
||||
$creatorChannelId = $this->getCreatorChannelId();
|
||||
if (!$this->auth->isSuperAdmin()) {
|
||||
if ($creatorChannelId === null || $creatorChannelId === '') {
|
||||
return $this->error(__('You have no permission'));
|
||||
}
|
||||
$data['channel_id'] = $creatorChannelId;
|
||||
$data['parent_admin_id'] = $this->auth->id;
|
||||
}
|
||||
$data['invite_code'] = $this->generateUniqueInviteCode();
|
||||
$requireCommissionRate = $this->requireCommissionRate($data['group_arr'] ?? []);
|
||||
if ($requireCommissionRate) {
|
||||
if (!$this->isValidCommissionRate($data['commission_rate'] ?? null)) {
|
||||
return $this->error(__('Please enter a valid commission rate for non-top role group'));
|
||||
}
|
||||
$commissionRes = $this->validateAdminCommissionByGroups($data['group_arr'] ?? [], floatval((string)$data['commission_rate']));
|
||||
if ($commissionRes !== null) return $commissionRes;
|
||||
} else {
|
||||
$data['commission_rate'] = null;
|
||||
}
|
||||
$result = false;
|
||||
if (!empty($data['group_arr'])) {
|
||||
$authRes = $this->checkGroupAuth($data['group_arr']);
|
||||
@@ -230,6 +263,37 @@ class Admin extends Backend
|
||||
if (!$data) {
|
||||
return $this->error(__('Parameter %s can not be empty', ['']));
|
||||
}
|
||||
$data = $this->normalizeSingleGroup($data);
|
||||
if (!$this->hasSingleGroup($data['group_arr'] ?? null)) {
|
||||
return $this->error('请选择且仅选择一个角色组');
|
||||
}
|
||||
|
||||
// 未提交分红比例时,若角色组未变更则沿用数据库原值(避免表单单项 number 校验把空串判错)
|
||||
$postedGroups = array_map('intval', $data['group_arr'] ?? []);
|
||||
$rowGroups = array_map('intval', $row->group_arr ?? []);
|
||||
sort($postedGroups);
|
||||
sort($rowGroups);
|
||||
$sameGroups = $postedGroups === $rowGroups;
|
||||
$postedCommission = $data['commission_rate'] ?? null;
|
||||
if (($postedCommission === null || $postedCommission === '') && $sameGroups && $this->isValidCommissionRate($row['commission_rate'] ?? null)) {
|
||||
$data['commission_rate'] = $row['commission_rate'];
|
||||
}
|
||||
|
||||
// 当前管理员编辑自身时,不允许修改角色组和分红比
|
||||
if ((int)$this->auth->id === (int)$id) {
|
||||
$postedGroups = $data['group_arr'] ?? [];
|
||||
if (!is_array($postedGroups)) {
|
||||
$postedGroups = [];
|
||||
}
|
||||
$originGroups = $row->group_arr ?? [];
|
||||
sort($postedGroups);
|
||||
sort($originGroups);
|
||||
$postedRate = $data['commission_rate'] ?? null;
|
||||
$originRate = $row['commission_rate'] ?? null;
|
||||
if ($postedGroups !== $originGroups || (string)$postedRate !== (string)$originRate) {
|
||||
return $this->error(__('You cannot modify your own management group!'));
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->modelValidate) {
|
||||
try {
|
||||
@@ -276,15 +340,29 @@ class Admin extends Backend
|
||||
if ($authRes !== null) return $authRes;
|
||||
}
|
||||
|
||||
Db::name('admin_group_access')
|
||||
->where('uid', $id)
|
||||
->delete();
|
||||
|
||||
$data = $this->excludeFields($data);
|
||||
unset($data['invite_code']);
|
||||
$creatorChannelId = $this->getCreatorChannelId();
|
||||
if (!$this->auth->isSuperAdmin() && $creatorChannelId !== null && $creatorChannelId !== '') {
|
||||
$data['channel_id'] = $creatorChannelId;
|
||||
}
|
||||
$requireCommissionRate = $this->requireCommissionRate($data['group_arr'] ?? []);
|
||||
if ($requireCommissionRate) {
|
||||
if (!$this->isValidCommissionRate($data['commission_rate'] ?? null)) {
|
||||
return $this->error(__('Please enter a valid commission rate for non-top role group'));
|
||||
}
|
||||
$commissionRes = $this->validateAdminCommissionByGroups($data['group_arr'] ?? [], floatval((string)$data['commission_rate']), intval((string)$id));
|
||||
if ($commissionRes !== null) return $commissionRes;
|
||||
} else {
|
||||
$data['commission_rate'] = null;
|
||||
}
|
||||
$result = false;
|
||||
$this->model->startTrans();
|
||||
try {
|
||||
$result = $row->save($data);
|
||||
Db::name('admin_group_access')
|
||||
->where('uid', $id)
|
||||
->delete();
|
||||
if ($groupAccess) {
|
||||
Db::name('admin_group_access')->insertAll($groupAccess);
|
||||
}
|
||||
@@ -357,7 +435,7 @@ class Admin extends Backend
|
||||
if ($this->auth->isSuperAdmin()) {
|
||||
return null;
|
||||
}
|
||||
$authGroups = $this->auth->getAllAuthGroups('allAuthAndOthers');
|
||||
$authGroups = $this->getManageableGroupIds();
|
||||
foreach ($groups as $group) {
|
||||
if (!in_array($group, $authGroups)) {
|
||||
return $this->error(__('You have no permission to add an administrator to this group!'));
|
||||
@@ -365,4 +443,132 @@ class Admin extends Backend
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private function getManageableGroupIds(): array
|
||||
{
|
||||
if ($this->auth->isSuperAdmin()) {
|
||||
return Db::name('admin_group')->where('status', 1)->column('id');
|
||||
}
|
||||
$own = Db::name('admin_group_access')->where('uid', $this->auth->id)->column('group_id');
|
||||
$children = $this->auth->getAdminChildGroups();
|
||||
return array_values(array_unique(array_merge($own, $children)));
|
||||
}
|
||||
|
||||
private function getCreatorChannelId(): mixed
|
||||
{
|
||||
$currentAdmin = Db::name('admin')
|
||||
->field(['id', 'channel_id'])
|
||||
->where('id', $this->auth->id)
|
||||
->find();
|
||||
if ($currentAdmin && !empty($currentAdmin['channel_id'])) {
|
||||
return $currentAdmin['channel_id'];
|
||||
}
|
||||
$channelId = Db::name('channel')
|
||||
->where('top_admin_id', $this->auth->id)
|
||||
->value('id');
|
||||
return $channelId ?: null;
|
||||
}
|
||||
|
||||
private function generateUniqueInviteCode(): string
|
||||
{
|
||||
$tries = 0;
|
||||
do {
|
||||
$tries++;
|
||||
$code = strtoupper(substr(Random::uuid(), 0, 8));
|
||||
$exists = Db::name('admin')->where('invite_code', $code)->find();
|
||||
} while ($exists && $tries < 20);
|
||||
return $code;
|
||||
}
|
||||
|
||||
private function requireCommissionRate(array $groupIds): bool
|
||||
{
|
||||
if (!$groupIds) {
|
||||
return false;
|
||||
}
|
||||
$count = Db::name('admin_group')
|
||||
->where('id', 'in', $groupIds)
|
||||
->where('pid', '<>', 0)
|
||||
->count();
|
||||
return $count > 0;
|
||||
}
|
||||
|
||||
private function isValidCommissionRate(mixed $value): bool
|
||||
{
|
||||
if ($value === null || $value === '') {
|
||||
return false;
|
||||
}
|
||||
$rate = trim((string)$value);
|
||||
if (!preg_match('/^(100(\.00?)?|[0-9]{1,2}(\.[0-9]{1,2})?)$/', $rate)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private function validateAdminCommissionByGroups(array $groupIds, float $currentRate, ?int $excludeAdminId = null): ?Response
|
||||
{
|
||||
if (!$groupIds) {
|
||||
return null;
|
||||
}
|
||||
$groups = Db::name('admin_group')
|
||||
->where('id', 'in', $groupIds)
|
||||
->where('pid', '<>', 0)
|
||||
->column('name', 'id');
|
||||
foreach ($groups as $groupId => $groupName) {
|
||||
$query = Db::name('admin_group_access')->alias('aga')
|
||||
->join('admin a', 'aga.uid = a.id')
|
||||
->where('aga.group_id', intval((string)$groupId));
|
||||
if ($excludeAdminId !== null) {
|
||||
$query = $query->where('a.id', '<>', $excludeAdminId);
|
||||
}
|
||||
$sum = (float)$query->sum('a.commission_rate');
|
||||
$remaining = 100 - $sum;
|
||||
if ($currentRate > $remaining + 0.000001) {
|
||||
$exceed = $currentRate - $remaining;
|
||||
return $this->error(sprintf('角色组[%s]分红比例总和不能超过100%%,当前剩余 %.2f%%,本次超出 %.2f%%', $groupName, max(0, $remaining), $exceed));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private function getTopGroupUserMap(array $userIds): array
|
||||
{
|
||||
if (!$userIds) {
|
||||
return [];
|
||||
}
|
||||
$uids = Db::name('admin_group_access')->alias('aga')
|
||||
->join('admin_group ag', 'aga.group_id = ag.id')
|
||||
->where('aga.uid', 'in', $userIds)
|
||||
->where('ag.pid', 0)
|
||||
->column('aga.uid');
|
||||
$map = [];
|
||||
foreach ($uids as $uid) {
|
||||
$map[$uid] = true;
|
||||
}
|
||||
return $map;
|
||||
}
|
||||
|
||||
private function normalizeSingleGroup(array $data): array
|
||||
{
|
||||
if (!array_key_exists('group_arr', $data)) {
|
||||
return $data;
|
||||
}
|
||||
$group = $data['group_arr'];
|
||||
if (is_array($group)) {
|
||||
$data['group_arr'] = array_values(array_filter($group, static function ($item) {
|
||||
return $item !== null && $item !== '';
|
||||
}));
|
||||
return $data;
|
||||
}
|
||||
if ($group === null || $group === '') {
|
||||
$data['group_arr'] = [];
|
||||
return $data;
|
||||
}
|
||||
$data['group_arr'] = [$group];
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function hasSingleGroup(mixed $groups): bool
|
||||
{
|
||||
return is_array($groups) && count($groups) === 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ class User extends Backend
|
||||
|
||||
protected array|string $preExcludeFields = ['id', 'uuid', 'create_time', 'update_time'];
|
||||
|
||||
protected array $withJoinTable = ['gameChannel', 'admin'];
|
||||
protected array $withJoinTable = ['channel', 'admin'];
|
||||
|
||||
protected string|array $quickSearchField = ['id', 'username', 'phone'];
|
||||
|
||||
@@ -53,7 +53,7 @@ class User extends Backend
|
||||
$data['password'] = hash_password($password);
|
||||
|
||||
$username = $data['username'] ?? '';
|
||||
$channelId = $data['channel_id'] ?? ($data['game_channel_id'] ?? null);
|
||||
$channelId = $data['channel_id'] ?? null;
|
||||
if (!is_string($username) || trim($username) === '' || $channelId === null || $channelId === '') {
|
||||
return $this->error(__('Parameter %s can not be empty', ['username/channel_id']));
|
||||
}
|
||||
@@ -131,10 +131,8 @@ class User extends Backend
|
||||
$nextChannelId = null;
|
||||
if (array_key_exists('channel_id', $data)) {
|
||||
$nextChannelId = $data['channel_id'];
|
||||
} elseif (array_key_exists('game_channel_id', $data)) {
|
||||
$nextChannelId = $data['game_channel_id'];
|
||||
} else {
|
||||
$nextChannelId = $row['channel_id'] ?? $row['game_channel_id'] ?? null;
|
||||
$nextChannelId = $row['channel_id'] ?? null;
|
||||
}
|
||||
|
||||
if (is_string($nextUsername) && trim($nextUsername) !== '' && $nextChannelId !== null && $nextChannelId !== '') {
|
||||
@@ -194,7 +192,7 @@ class User extends Backend
|
||||
$res = $this->model
|
||||
->withJoin($this->withJoinTable, $this->withJoinType)
|
||||
->with($this->withJoinTable)
|
||||
->visible(['gameChannel' => ['name'], 'admin' => ['username']])
|
||||
->visible(['channel' => ['name'], 'admin' => ['username']])
|
||||
->alias($alias)
|
||||
->where($where)
|
||||
->order($order)
|
||||
|
||||
Reference in New Issue
Block a user