1.优化后端管理员提现方式

2.优化后端
This commit is contained in:
2026-04-23 14:11:55 +08:00
parent aa1299c018
commit 378be9909d
9 changed files with 362 additions and 175 deletions

View File

@@ -21,16 +21,16 @@ class Dashboard extends Backend
return $response;
}
$scope = $this->channelScopeOrNull();
$ownerAdminId = $this->ownerAdminIdOrNull();
$todayStart = strtotime(date('Y-m-d'));
$todayEnd = $todayStart + 86400 - 1;
$yesterdayStart = $todayStart - 86400;
$yesterdayEnd = $todayStart - 1;
$userTotal = $this->countUsers($scope);
$newToday = $this->countUsersInRange($scope, $todayStart, $todayEnd);
$newYesterday = $this->countUsersInRange($scope, $yesterdayStart, $yesterdayEnd);
$userTotal = $this->countUsers($ownerAdminId);
$newToday = $this->countUsersInRange($ownerAdminId, $todayStart, $todayEnd);
$newYesterday = $this->countUsersInRange($ownerAdminId, $yesterdayStart, $yesterdayEnd);
$growthPct = null;
if ($newYesterday > 0) {
$growthPct = round(($newToday - $newYesterday) / $newYesterday * 100, 1);
@@ -38,14 +38,14 @@ class Dashboard extends Backend
$growthPct = 100.0;
}
$depositAgg = $this->aggregateDepositToday($scope, $todayStart, $todayEnd);
$withdrawPending = $this->countWithdrawPending($scope);
$betAgg = $this->aggregateBetToday($scope, $todayStart, $todayEnd);
$depositAgg = $this->aggregateDepositToday($ownerAdminId, $todayStart, $todayEnd);
$withdrawPending = $this->countWithdrawPending($ownerAdminId);
$betAgg = $this->aggregateBetToday($ownerAdminId, $todayStart, $todayEnd);
$trend = $this->buildSevenDayTrend($scope);
$channelShare = $this->buildChannelShare($scope);
$depositAmountChannelShare = $this->buildDepositAmountChannelShare($scope);
$recentUsers = $this->fetchRecentUsers($scope, 10);
$trend = $this->buildSevenDayTrend($ownerAdminId);
$channelShare = $this->buildChannelShare($ownerAdminId);
$depositAmountChannelShare = $this->buildDepositAmountChannelShare($ownerAdminId);
$recentUsers = $this->fetchRecentUsers($ownerAdminId, 10);
return $this->success('', [
'remark' => get_route_remark(),
@@ -68,27 +68,27 @@ class Dashboard extends Backend
}
/**
* @param int[]|null $scope null=超管不限制;非 null 时 whereIn channel_id
* @param int|null $ownerAdminId null=超管不限制;非 null 时 where admin_id
*/
private function countUsers(?array $scope): int
private function countUsers(?int $ownerAdminId): int
{
$q = Db::name('user');
if ($scope !== null) {
$q->whereIn('channel_id', $scope);
if ($ownerAdminId !== null) {
$q->where('admin_id', '=', $ownerAdminId);
}
return intval($q->count());
}
/**
* @param int[]|null $scope
* @param int|null $ownerAdminId
*/
private function countUsersInRange(?array $scope, int $start, int $end): int
private function countUsersInRange(?int $ownerAdminId, int $start, int $end): int
{
$q = Db::name('user')
->where('create_time', '>=', $start)
->where('create_time', '<=', $end);
if ($scope !== null) {
$q->whereIn('channel_id', $scope);
if ($ownerAdminId !== null) {
$q->where('admin_id', '=', $ownerAdminId);
}
return intval($q->count());
}
@@ -96,17 +96,17 @@ class Dashboard extends Backend
/**
* 今日成功充值status=1按创建日落在今日与 mock 即时成功一致)。
*
* @param int[]|null $scope
* @param int|null $ownerAdminId
* @return array{count:int, amount:string}
*/
private function aggregateDepositToday(?array $scope, int $todayStart, int $todayEnd): array
private function aggregateDepositToday(?int $ownerAdminId, int $todayStart, int $todayEnd): array
{
$q = Db::name('deposit_order')
->where('status', 1)
->where('create_time', '>=', $todayStart)
->where('create_time', '<=', $todayEnd);
if ($scope !== null) {
$q->whereIn('channel_id', $scope);
if ($ownerAdminId !== null) {
$q->whereIn('user_id', $this->scopedUserIds($ownerAdminId));
}
$rows = $q->fieldRaw('COUNT(*) AS c, COALESCE(SUM(CAST(amount AS DECIMAL(18,2))),0) AS s')->find();
if (!is_array($rows)) {
@@ -120,13 +120,13 @@ class Dashboard extends Backend
}
/**
* @param int[]|null $scope
* @param int|null $ownerAdminId
*/
private function countWithdrawPending(?array $scope): int
private function countWithdrawPending(?int $ownerAdminId): int
{
$q = Db::name('withdraw_order')->where('status', 0);
if ($scope !== null) {
$q->whereIn('channel_id', $scope);
if ($ownerAdminId !== null) {
$q->whereIn('user_id', $this->scopedUserIds($ownerAdminId));
}
return intval($q->count());
}
@@ -134,17 +134,17 @@ class Dashboard extends Backend
/**
* 今日投注创建时间在今日且订单未作废status 1 或 2
*
* @param int[]|null $scope
* @param int|null $ownerAdminId
* @return array{count:int, amount:string}
*/
private function aggregateBetToday(?array $scope, int $todayStart, int $todayEnd): array
private function aggregateBetToday(?int $ownerAdminId, int $todayStart, int $todayEnd): array
{
$q = Db::name('bet_order')
->whereIn('status', [1, 2])
->where('create_time', '>=', $todayStart)
->where('create_time', '<=', $todayEnd);
if ($scope !== null) {
$q->whereIn('channel_id', $scope);
if ($ownerAdminId !== null) {
$q->whereIn('user_id', $this->scopedUserIds($ownerAdminId));
}
$rows = $q->fieldRaw('COUNT(*) AS c, COALESCE(SUM(CAST(total_amount AS DECIMAL(18,2))),0) AS s')->find();
if (!is_array($rows)) {
@@ -157,10 +157,10 @@ class Dashboard extends Backend
}
/**
* @param int[]|null $scope
* @param int|null $ownerAdminId
* @return array{days:string[], new_users:int[], deposit_amount:string[], bet_amount:string[]}
*/
private function buildSevenDayTrend(?array $scope): array
private function buildSevenDayTrend(?int $ownerAdminId): array
{
$days = [];
$newUsers = [];
@@ -172,14 +172,14 @@ class Dashboard extends Backend
$dayEnd = $dayStart + 86400 - 1;
$days[] = date('m-d', $dayStart);
$newUsers[] = $this->countUsersInRange($scope, $dayStart, $dayEnd);
$newUsers[] = $this->countUsersInRange($ownerAdminId, $dayStart, $dayEnd);
$dq = Db::name('deposit_order')
->where('status', 1)
->where('create_time', '>=', $dayStart)
->where('create_time', '<=', $dayEnd);
if ($scope !== null) {
$dq->whereIn('channel_id', $scope);
if ($ownerAdminId !== null) {
$dq->whereIn('user_id', $this->scopedUserIds($ownerAdminId));
}
$drow = $dq->fieldRaw('COALESCE(SUM(CAST(amount AS DECIMAL(18,2))),0) AS s')->find();
$dsum = is_array($drow) && isset($drow['s']) ? strval($drow['s']) : '0';
@@ -189,8 +189,8 @@ class Dashboard extends Backend
->whereIn('status', [1, 2])
->where('create_time', '>=', $dayStart)
->where('create_time', '<=', $dayEnd);
if ($scope !== null) {
$bq->whereIn('channel_id', $scope);
if ($ownerAdminId !== null) {
$bq->whereIn('user_id', $this->scopedUserIds($ownerAdminId));
}
$brow = $bq->fieldRaw('COALESCE(SUM(CAST(total_amount AS DECIMAL(18,2))),0) AS s')->find();
$bsum = is_array($brow) && isset($brow['s']) ? strval($brow['s']) : '0';
@@ -208,14 +208,14 @@ class Dashboard extends Backend
/**
* 用户按渠道分布(取前 8 名,其余合并为「其他」)。
*
* @param int[]|null $scope
* @param int|null $ownerAdminId
* @return list<array{name:string, value:int}>
*/
private function buildChannelShare(?array $scope): array
private function buildChannelShare(?int $ownerAdminId): array
{
$q = Db::name('user')->fieldRaw('channel_id, COUNT(*) AS c')->group('channel_id');
if ($scope !== null) {
$q->whereIn('channel_id', $scope);
if ($ownerAdminId !== null) {
$q->where('admin_id', '=', $ownerAdminId);
}
$rows = $q->orderRaw('c DESC')->select()->toArray();
if ($rows === []) {
@@ -257,17 +257,17 @@ class Dashboard extends Backend
/**
* 成功充值金额按订单归属渠道汇总status=1受渠道范围限制
*
* @param int[]|null $scope
* @param int|null $ownerAdminId
* @return list<array{name:string, value:string}> value 为两位小数字符串,供前端饼图展示
*/
private function buildDepositAmountChannelShare(?array $scope): array
private function buildDepositAmountChannelShare(?int $ownerAdminId): array
{
$q = Db::name('deposit_order')
->where('status', 1)
->fieldRaw('channel_id, COALESCE(SUM(CAST(amount AS DECIMAL(18,2))),0) AS s')
->group('channel_id');
if ($scope !== null) {
$q->whereIn('channel_id', $scope);
if ($ownerAdminId !== null) {
$q->whereIn('user_id', $this->scopedUserIds($ownerAdminId));
}
$rows = $q->select()->toArray();
if ($rows === []) {
@@ -322,17 +322,17 @@ class Dashboard extends Backend
}
/**
* @param int[]|null $scope
* @param int|null $ownerAdminId
* @return list<array{id:int, username:string, create_time:int, channel_name:string, head_image:string}>
*/
private function fetchRecentUsers(?array $scope, int $limit): array
private function fetchRecentUsers(?int $ownerAdminId, int $limit): array
{
$q = Db::name('user')
->field(['id', 'username', 'create_time', 'channel_id', 'head_image'])
->order('id', 'desc')
->limit($limit);
if ($scope !== null) {
$q->whereIn('channel_id', $scope);
if ($ownerAdminId !== null) {
$q->where('admin_id', '=', $ownerAdminId);
}
$rows = $q->select()->toArray();
if ($rows === []) {
@@ -372,22 +372,31 @@ class Dashboard extends Backend
}
/**
* 非超管:按管理员所属渠道过滤;未绑定渠道时按 channel_id IN (0) 与列表页一致
* 超管:返回 null表示 SQL 不加渠道条件。
* 非超管:按当前管理员名下用户过滤
* 超管:返回 null表示 SQL 不加管理员条件。
*
* @return int[]|null
* @return int|null
*/
private function channelScopeOrNull(): ?array
private function ownerAdminIdOrNull(): ?int
{
if (!$this->auth || $this->auth->isSuperAdmin()) {
return null;
}
$admin = Db::name('admin')->field(['id', 'channel_id'])->where('id', $this->auth->id)->find();
$ids = [];
if ($admin && !empty($admin['channel_id'])) {
$ids[] = $admin['channel_id'];
$idRaw = $this->auth->id;
if ($idRaw === null || $idRaw === '' || !is_numeric(strval($idRaw))) {
return 0;
}
$id = intval(strval($idRaw));
return $id > 0 ? $id : 0;
}
return $ids !== [] ? array_values(array_unique($ids)) : [0];
/**
* @return int[]
*/
private function scopedUserIds(int $ownerAdminId): array
{
$ids = Db::name('user')->where('admin_id', '=', $ownerAdminId)->column('id');
$ids = array_map('intval', $ids);
return $ids === [] ? [0] : array_values(array_unique($ids));
}
}

View File

@@ -32,4 +32,32 @@ class UserNoticeRead extends Backend
$this->model = new \app\common\model\UserNoticeRead();
return null;
}
protected function _index(): Response
{
if ($this->request && $this->request->get('select')) {
return $this->select($this->request);
}
list($where, $alias, $limit, $order) = $this->queryBuilder();
$table = strtolower($this->model->getTable());
$mainShort = $alias[$table] ?? '';
if ($mainShort !== '' && $this->auth && !$this->auth->isSuperAdmin()) {
$where[] = ['user.admin_id', '=', intval(strval($this->auth->id))];
}
$res = $this->model
->withJoin($this->withJoinTable, $this->withJoinType)
->with($this->withJoinTable)
->alias($alias)
->where($where)
->order($order)
->paginate($limit);
return $this->success('', [
'list' => $res->items(),
'total' => $res->total(),
'remark' => get_route_remark(),
]);
}
}

View File

@@ -3,7 +3,6 @@
namespace app\admin\controller\order;
use app\common\controller\Backend;
use support\think\Db;
use support\Response;
use Webman\Http\Request as WebmanRequest;
@@ -79,8 +78,7 @@ class BetOrder extends Backend
$table = strtolower($this->model->getTable());
$mainShort = $alias[$table] ?? '';
if ($mainShort !== '' && $this->auth && !$this->auth->isSuperAdmin()) {
$channelIds = $this->getScopedChannelIdsForFilter();
$where[] = [$mainShort . '.channel_id', 'in', $channelIds !== [] ? $channelIds : [0]];
$where[] = ['user.admin_id', '=', intval(strval($this->auth->id))];
}
$res = $this->model
@@ -103,25 +101,4 @@ class BetOrder extends Backend
]);
}
/**
* @return int[]
*/
private function getScopedChannelIdsForFilter(): array
{
if (!$this->auth) {
return [0];
}
if ($this->auth->isSuperAdmin()) {
return [];
}
$admin = Db::name('admin')
->field(['id', 'channel_id'])
->where('id', $this->auth->id)
->find();
$ids = [];
if ($admin && !empty($admin['channel_id'])) {
$ids[] = $admin['channel_id'];
}
return array_values(array_unique($ids));
}
}

View File

@@ -3,7 +3,6 @@
namespace app\admin\controller\order;
use app\common\controller\Backend;
use support\think\Db;
use support\Response;
use Webman\Http\Request as WebmanRequest;
@@ -49,8 +48,7 @@ class DepositOrder extends Backend
$table = strtolower($this->model->getTable());
$mainShort = $alias[$table] ?? '';
if ($mainShort !== '' && $this->auth && !$this->auth->isSuperAdmin()) {
$channelIds = $this->getScopedChannelIdsForFilter();
$where[] = [$mainShort . '.channel_id', 'in', $channelIds !== [] ? $channelIds : [0]];
$where[] = ['user.admin_id', '=', intval(strval($this->auth->id))];
}
$this->appendDepositOrderIndexWhere($where, $mainShort);
@@ -115,7 +113,7 @@ class DepositOrder extends Backend
->withJoin($this->withJoinTable, $this->withJoinType)
->with($this->withJoinTable)
->visible([
'user' => ['username', 'phone'],
'user' => ['username', 'phone', 'admin_id'],
'channel' => ['name'],
])
->where($this->model->getTable() . '.id', $id)
@@ -131,36 +129,18 @@ class DepositOrder extends Backend
if (!$this->auth || $this->auth->isSuperAdmin()) {
return true;
}
$channelIds = $this->getScopedChannelIdsForFilter();
if ($channelIds === []) {
$userRow = $row['user'] ?? null;
if (!is_array($userRow)) {
return false;
}
$raw = $row['channel_id'] ?? null;
if ($raw === null || $raw === '') {
$adminIdRaw = $userRow['admin_id'] ?? null;
if ($adminIdRaw === null || $adminIdRaw === '') {
return false;
}
if (!is_numeric(strval($raw))) {
if (!is_numeric(strval($adminIdRaw))) {
return false;
}
return in_array(intval(strval($raw)), $channelIds, true);
return intval(strval($adminIdRaw)) === intval(strval($this->auth->id));
}
/**
* @return int[]
*/
private function getScopedChannelIdsForFilter(): array
{
if (!$this->auth) {
return [0];
}
if ($this->auth->isSuperAdmin()) {
return [];
}
$admin = Db::name('admin')->field(['id', 'channel_id'])->where('id', $this->auth->id)->find();
$ids = [];
if ($admin && !empty($admin['channel_id'])) {
$ids[] = $admin['channel_id'];
}
return array_values(array_unique($ids));
}
}

View File

@@ -48,8 +48,7 @@ class WithdrawOrder extends Backend
$table = strtolower($this->model->getTable());
$mainShort = $alias[$table] ?? '';
if ($mainShort !== '' && $this->auth && !$this->auth->isSuperAdmin()) {
$channelIds = $this->getScopedChannelIdsForFilter();
$where[] = [$mainShort . '.channel_id', 'in', $channelIds !== [] ? $channelIds : [0]];
$where[] = ['user.admin_id', '=', intval(strval($this->auth->id))];
}
$res = $this->model
@@ -386,17 +385,17 @@ class WithdrawOrder extends Backend
if (!$this->auth || $this->auth->isSuperAdmin()) {
return true;
}
$channelIds = $this->getScopedChannelIdsForFilter();
if ($channelIds === []) {
$uidRaw = is_array($row) ? ($row['user_id'] ?? null) : ($row->user_id ?? null);
$uid = $this->intParam($uidRaw);
if ($uid <= 0) {
return false;
}
$raw = is_array($row) ? ($row['channel_id'] ?? null) : ($row->channel_id ?? null);
if ($raw === null || $raw === '') {
// 无归属渠道的数据只有超管可见
$user = Db::name('user')->field(['id', 'admin_id'])->where('id', $uid)->find();
if (!is_array($user)) {
return false;
}
$cid = $this->intParam($raw);
return in_array($cid, $channelIds, true);
$ownerAdminId = $this->intParam($user['admin_id'] ?? 0);
return $ownerAdminId > 0 && $ownerAdminId === $this->intParam($this->auth->id ?? 0);
}
private function intParam($raw): int
@@ -453,22 +452,4 @@ class WithdrawOrder extends Backend
return $negative ? ('-' . $v) : $v;
}
/**
* @return int[]
*/
private function getScopedChannelIdsForFilter(): array
{
if (!$this->auth) {
return [0];
}
if ($this->auth->isSuperAdmin()) {
return [];
}
$admin = Db::name('admin')->field(['id', 'channel_id'])->where('id', $this->auth->id)->find();
$ids = [];
if ($admin && !empty($admin['channel_id'])) {
$ids[] = $admin['channel_id'];
}
return array_values(array_unique($ids));
}
}

View File

@@ -15,6 +15,11 @@ use Webman\Http\Request as WebmanRequest;
*/
class User extends Backend
{
/**
* 这些接口只要求登录,不单独校验权限节点
*/
protected array $noNeedPermission = ['adminScopeTree'];
/**
* User模型对象
* @var object|null
@@ -22,6 +27,11 @@ class User extends Backend
*/
protected ?object $model = null;
/**
* 渠道管理员仅可管理自己名下admin_id=当前管理员)的用户
*/
protected bool|string|int $dataLimit = true;
protected array|string $preExcludeFields = ['id', 'uuid', 'create_time', 'update_time', 'invite_code', 'coin', 'total_deposit_coin', 'total_withdraw_coin', 'bet_flow_coin'];
protected array $withJoinTable = ['channel', 'admin'];
@@ -390,9 +400,17 @@ class User extends Backend
return $response;
}
$currentAdminLeaf = $this->getCurrentAdminLeaf();
if ($currentAdminLeaf === null) {
return $this->error(__('Record not found'));
}
$groupIds = $this->getManageableAdminGroupIds();
if ($groupIds === []) {
return $this->success('', ['list' => []]);
return $this->success('', [
'list' => [$currentAdminLeaf],
'current_admin_id' => $currentAdminLeaf['value'],
]);
}
$groups = Db::name('admin_group')
@@ -488,12 +506,45 @@ class User extends Backend
foreach ($roots as $rid) {
$tree[] = $buildNode($rid);
}
if (!isset($adminPrimary[intval(strval($currentAdminLeaf['value']))])) {
$tree[] = $currentAdminLeaf;
}
return $this->success('', [
'list' => $tree,
'current_admin_id' => $currentAdminLeaf['value'],
]);
}
private function getCurrentAdminLeaf(): ?array
{
$adminIdRaw = $this->auth->id ?? null;
if ($adminIdRaw === null || $adminIdRaw === '' || !is_numeric(strval($adminIdRaw))) {
return null;
}
$adminId = intval(strval($adminIdRaw));
if ($adminId <= 0) {
return null;
}
$row = Db::name('admin')
->field(['id', 'username', 'channel_id', 'invite_code'])
->where('id', $adminId)
->find();
if (!$row) {
return null;
}
$invite = $row['invite_code'] ?? '';
$invite = is_string($invite) ? $invite : '';
$channelId = $row['channel_id'] ?? null;
return [
'value' => strval($adminId),
'label' => strval($row['username'] ?? ('#' . strval($adminId))),
'is_leaf' => true,
'channel_id' => $channelId === null || $channelId === '' ? null : intval(strval($channelId)),
'invite_code' => $invite,
];
}
/**
* @return int[]
*/

View File

@@ -3,7 +3,6 @@
namespace app\admin\controller\user;
use app\common\controller\Backend;
use support\think\Db;
use support\Response;
use Webman\Http\Request as WebmanRequest;
@@ -79,8 +78,7 @@ class UserWalletRecord extends Backend
$table = strtolower($this->model->getTable());
$mainShort = $alias[$table] ?? '';
if ($mainShort !== '' && $this->auth && !$this->auth->isSuperAdmin()) {
$channelIds = $this->getScopedChannelIdsForFilter();
$where[] = [$mainShort . '.channel_id', 'in', $channelIds !== [] ? $channelIds : [0]];
$where[] = ['user.admin_id', '=', intval(strval($this->auth->id))];
}
$res = $this->model
@@ -103,27 +101,4 @@ class UserWalletRecord extends Backend
]);
}
/**
* 非超管:与渠道管理一致,仅本账号相关渠道
*
* @return int[]
*/
private function getScopedChannelIdsForFilter(): array
{
if (!$this->auth) {
return [0];
}
if ($this->auth->isSuperAdmin()) {
return [];
}
$admin = Db::name('admin')
->field(['id', 'channel_id'])
->where('id', $this->auth->id)
->find();
$ids = [];
if ($admin && !empty($admin['channel_id'])) {
$ids[] = $admin['channel_id'];
}
return array_values(array_unique($ids));
}
}