[游戏管理]用户管理

This commit is contained in:
2026-04-15 10:19:30 +08:00
parent 66c14eacb3
commit 7b002c9410
11 changed files with 386 additions and 28 deletions

View File

@@ -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;
}
}