Просмотр исходного кода

后台列表补充会员账号 account 展示与筛选

- 余额日志、在线用户、体育订单、三方游戏订单、三方入款、代收/人工充值、
  USDT 充值、USDT/人民币提现、余额宝、活动用户、返水、回流、兑换列表:
  返回 account,支持 account 模糊筛选
- 登录日志:支持 account 筛选(同时匹配登录时输入的账号)
- 新增 App\Support\MemberAccount 处理未 join users 的列表筛选
- 测试夹具 users 表补 account 列;新增 AdminListAccountTest

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
doge 8 часов назад
Родитель
Сommit
77df57ae63
36 измененных файлов с 218 добавлено и 19 удалено
  1. 4 1
      app/Http/Controllers/admin/ActivityUser.php
  2. 1 0
      app/Http/Controllers/admin/Backflow.php
  3. 2 1
      app/Http/Controllers/admin/Balance.php
  4. 1 0
      app/Http/Controllers/admin/Online.php
  5. 5 1
      app/Http/Controllers/admin/Operation.php
  6. 5 1
      app/Http/Controllers/admin/Order.php
  7. 1 0
      app/Http/Controllers/admin/PaymentOrder.php
  8. 1 0
      app/Http/Controllers/admin/Rebate.php
  9. 1 0
      app/Http/Controllers/admin/ThirdGameOrder.php
  10. 1 0
      app/Http/Controllers/admin/ThirdPartyDeposit.php
  11. 9 0
      app/Http/Controllers/admin/User.php
  12. 1 0
      app/Http/Controllers/admin/Wallet.php
  13. 4 2
      app/Http/Controllers/admin/Withdraw.php
  14. 5 1
      app/Http/Controllers/admin/YueBao.php
  15. 1 1
      app/Models/ActivityUser.php
  16. 1 1
      app/Models/BalanceLog.php
  17. 1 1
      app/Models/Rebate.php
  18. 1 1
      app/Models/Recharge.php
  19. 1 1
      app/Models/Withdraw.php
  20. 4 1
      app/Services/BackflowService.php
  21. 3 0
      app/Services/BalanceLogService.php
  22. 7 1
      app/Services/OnlineUserService.php
  23. 4 1
      app/Services/PaymentOrderService.php
  24. 7 1
      app/Services/RebateService.php
  25. 3 0
      app/Services/RechargeService.php
  26. 5 0
      app/Services/ThirdGameOrderService.php
  27. 5 0
      app/Services/ThirdPartyDepositService.php
  28. 3 0
      app/Services/WithdrawService.php
  29. 21 0
      app/Support/MemberAccount.php
  30. 2 0
      docs/admin-finance-api.md
  31. 2 0
      docs/admin-online-register-api.md
  32. 2 0
      docs/admin-third-game-order-api.md
  33. 101 0
      tests/Integration/AdminListAccountTest.php
  34. 1 1
      tests/Integration/AdminRealtimeTest.php
  35. 1 1
      tests/Integration/SharingSettlementTest.php
  36. 1 1
      tests/Integration/ThirdGamePlayerIdentityTest.php

+ 4 - 1
app/Http/Controllers/admin/ActivityUser.php

@@ -8,6 +8,7 @@ use App\Models\Wallet as WalletModel;
 use App\Services\ActivityUserService;
 use App\Services\BalanceLogService;
 use App\Services\TopUpService;
+use App\Support\MemberAccount;
 use Illuminate\Http\JsonResponse;
 use Illuminate\Support\Facades\Cache;
 use Illuminate\Support\Facades\DB;
@@ -67,11 +68,12 @@ class ActivityUser extends Controller
                 'limit' => ['required', 'integer', 'min:1'],
                 'title' => ['nullable', 'string'],
                 'member_id' => ['nullable', 'string'],
+                'account' => ['nullable', 'string', 'max:64'],
             ]);
             $page = request()->input('page', 1);
             $limit = request()->input('limit', 10);
             $where = ActivityUserService::getWhere($params);
-            $query = ActivityUserModel::where($where);
+            $query = MemberAccount::filter(ActivityUserModel::where($where), $params['account'] ?? null);
             $count = $query->count();
             $list = $query->orderBy('status')
                 ->with(['member'])
@@ -82,6 +84,7 @@ class ActivityUser extends Controller
                 $item['gift_amount'] = floatval($item['gift_amount']);
                 $item['effective_betting_amount'] = floatval($item['effective_betting_amount']);
                 $item['betting_amount'] = floatval($item['betting_amount']);
+                $item['account'] = (string)($item['member']['account'] ?? '');
             }
 
             $result = ['total' => $count, 'data' => $list];

+ 1 - 0
app/Http/Controllers/admin/Backflow.php

@@ -37,6 +37,7 @@ class Backflow extends Controller
                 'page' => ['required', 'integer', 'min:1'],
                 'limit' => ['required', 'integer', 'min:1'],
                 'member_id' => ['nullable', 'string'],
+                'account' => ['nullable', 'string', 'max:64'],
                 'first_name' => ['nullable'],
                 'date' => ['nullable', 'date_format:Y-m'],
                 'username' => ['nullable', 'string'],

+ 2 - 1
app/Http/Controllers/admin/Balance.php

@@ -26,6 +26,7 @@ class Balance extends Controller
                 'change_type' => ['nullable', 'string', 'in:' . implode(',', BalanceLogService::$RW)],
                 'start_time' => ['nullable', 'date', 'date_format:Y-m-d', 'required_with:end_time'],
                 'end_time' => ['nullable', 'date', 'date_format:Y-m-d', 'required_with:start_time'],
+                'account' => ['nullable', 'string', 'max:64'],
                 'first_name' => ['nullable'],
                 'is_fancai' => ['nullable', 'integer','in:0,1'],
                 // 用户类型/注册来源:-1 tg用户,0 后台客服账号,1 真实用户,2 虚拟用户
@@ -45,7 +46,7 @@ class Balance extends Controller
             // 统计与列表共用筛选条件;clone 避免 count/sum 影响后续分页查询
             $data['total'] = (clone $query)->count();
             $data['total_amount'] = bcadd((clone $query)->sum('balance_logs.amount'), 0, 2);
-            $data['data'] = $query->select("balance_logs.*", "users.first_name", "users.admin_note as user_admin_note", "users.from as user_from")
+            $data['data'] = $query->select("balance_logs.*", "users.first_name", "users.account", "users.admin_note as user_admin_note", "users.from as user_from")
                 ->orderByDesc('balance_logs.id')
                 ->forPage($page, $limit)
                 ->with(['member'])

+ 1 - 0
app/Http/Controllers/admin/Online.php

@@ -21,6 +21,7 @@ class Online extends Controller
                 'page' => ['nullable', 'integer', 'min:1'],
                 'limit' => ['nullable', 'integer', 'min:1', 'max:200'],
                 'member_id' => ['nullable', 'string'],
+                'account' => ['nullable', 'string', 'max:64'],
                 'first_name' => ['nullable', 'string'],
                 'login_ip' => ['nullable', 'string'],
                 'platform' => ['nullable', 'string'],

+ 5 - 1
app/Http/Controllers/admin/Operation.php

@@ -25,6 +25,7 @@ class Operation extends Controller
                 'end_time' => ['nullable', 'date', 'date_format:Y-m-d', 'required_with:start_time'],
                 'user_id' => ['nullable', 'string'],
                 'member_id' => ['nullable'],
+                'account' => ['nullable', 'string', 'max:64'],
                 'first_name' => ['nullable'],
             ]);
             $page = request()->input('page', 1);
@@ -40,13 +41,16 @@ class Operation extends Controller
             if (!empty($params['member_id'])) {
                 $query = $query->where('users.member_id', $params['member_id']);
             }
+            if (!empty($params['account'])) {
+                $query = $query->where('users.account', 'like', '%' . trim((string)$params['account']) . '%');
+            }
             if (!empty($params['first_name'])) {
                 $query = $query->where('users.first_name', 'like', "%{$params['first_name']}%");
             }
 
             $count = $query->count();
             $list = $query->join('wallets', 'users.member_id', '=', 'wallets.member_id')
-                ->select(['users.id', 'users.user_id','users.member_id', 'users.first_name', 'wallets.available_balance as money'])
+                ->select(['users.id', 'users.user_id','users.member_id', 'users.account', 'users.first_name', 'wallets.available_balance as money'])
                 ->forPage($page, $limit)
                 ->orderByDesc("users.created_at")
                 ->get()->toArray();

+ 5 - 1
app/Http/Controllers/admin/Order.php

@@ -80,6 +80,7 @@ class Order extends Controller
                 'id' => ['nullable', 'string'],
                 'user_id' => ['nullable', 'string'],
                 'member_id' => ['nullable'],
+                'account' => ['nullable', 'string', 'max:64'],
                 'first_name' => ['nullable'],
                 'user_id' => ['nullable', 'string'],
                 'id' => ['nullable'],
@@ -155,6 +156,9 @@ class Order extends Controller
             if (!empty($params['member_id'])) {
                 $query = $query->where('users.member_id', $params['member_id']);
             }
+            if (!empty($params['account'])) {
+                $query = $query->where('users.account', 'like', '%' . trim((string)$params['account']) . '%');
+            }
             if (!empty($params['first_name'])) {
                 $query = $query->where('users.first_name', 'like', "%{$params['first_name']}%");
             }
@@ -166,7 +170,7 @@ class Order extends Controller
             $count = $query->distinct()->count('sport_order.id');
             $list = $query->distinct()
                 ->select(['sport_order.*','sport_odds.odd_name as odd_name', 'sport.league', 'sport.league_en', 'sport.home_team', 'sport.home_team_en','sport.home_team_logo', 'sport.guest_team', 'sport.guest_team_en','sport.guest_team_logo',
-                    'users.member_id','users.first_name','users.from as user_from'
+                    'users.member_id','users.account','users.first_name','users.from as user_from'
                 ])
                 ->forPage($page, $limit)
                 ->orderByDesc('sport_order.create_time')

+ 1 - 0
app/Http/Controllers/admin/PaymentOrder.php

@@ -201,6 +201,7 @@ class PaymentOrder extends Controller
                 'order_no' => ['nullable', 'string'],
                 'status' => ['nullable', 'integer'],
                 'member_id' => ['nullable', 'integer'],
+                'account' => ['nullable', 'string', 'max:64'],
                 'first_name' => ['nullable'],
                 'payment_type' => ['nullable', 'integer'],
                 'channel' => ['nullable', 'string'],

+ 1 - 0
app/Http/Controllers/admin/Rebate.php

@@ -26,6 +26,7 @@ class Rebate extends Controller
                 'member_id' => ['nullable', 'integer', 'min:1'],
                 'date' => ['nullable', 'date_format:Y-m-d'],
                 'username' => ['nullable', 'string'],
+                'account' => ['nullable', 'string', 'max:64'],
                 'first_name' => ['nullable', 'string'],
             ]);
             $result = RebateService::paginate($search);

+ 1 - 0
app/Http/Controllers/admin/ThirdGameOrder.php

@@ -20,6 +20,7 @@ class ThirdGameOrder extends Controller
                 'user_id' => ['nullable', 'integer', 'min:1'],
                 'member_id' => ['nullable', 'string', 'max:64'],
                 'username' => ['nullable', 'string', 'max:128'],
+                'account' => ['nullable', 'string', 'max:64'],
                 'first_name' => ['nullable', 'string', 'max:128'],
                 'player_id' => ['nullable', 'string', 'max:32'],
                 'platform' => ['nullable', 'string', 'max:32'],

+ 1 - 0
app/Http/Controllers/admin/ThirdPartyDeposit.php

@@ -17,6 +17,7 @@ class ThirdPartyDeposit extends Controller
                 'page' => ['nullable', 'integer', 'min:1'],
                 'limit' => ['nullable', 'integer', 'min:1', 'max:200'],
                 'member_id' => ['nullable', 'string', 'max:64'],
+                'account' => ['nullable', 'string', 'max:64'],
                 'payment_company' => ['nullable', 'string', 'in:sanjin,jd,no,zimu'],
                 'payment_method' => ['nullable', 'string', 'max:100'],
                 'channel' => ['nullable', 'string', 'max:50'],

+ 9 - 0
app/Http/Controllers/admin/User.php

@@ -502,6 +502,7 @@ class User extends Controller
                 'limit' => ['nullable', 'integer', 'min:1'],
                 'user_id' => ['nullable'],
                 'member_id' => ['nullable'],
+                'account' => ['nullable', 'string', 'max:64'],
                 'first_name' => ['nullable'],
                 'status' => ['nullable', 'integer', 'in:0,1'],
                 'start_time' => ['nullable', 'date', 'date_format:Y-m-d', 'required_with:end_time'],
@@ -540,6 +541,14 @@ class User extends Controller
             if (!empty($params['member_id'])) {
                 $query->where('user_login.user_id', $params['member_id']);
             }
+            if (!empty($params['account'])) {
+                // 登录失败时可能匹配不到会员,同时按登录时输入的账号筛选
+                $account = '%' . trim((string)$params['account']) . '%';
+                $query->where(function ($q) use ($account) {
+                    $q->where('users.account', 'like', $account)
+                        ->orWhere('user_login.login_account', 'like', $account);
+                });
+            }
             if (!empty($params['first_name'])) {
                 $query->where('users.first_name', 'like', "%{$params['first_name']}%");
             }

+ 1 - 0
app/Http/Controllers/admin/Wallet.php

@@ -452,6 +452,7 @@ class Wallet extends Controller
                 'member_id' => ['nullable', 'string', 'min:1'],
                 'status' => ['nullable', 'string', 'min:0', 'max:3'],
                 'type' => ['nullable', 'integer', 'in:1,2'],
+                'account' => ['nullable', 'string', 'max:64'],
                 'first_name' => ['nullable'],
             ]);
             $search = request()->all();

+ 4 - 2
app/Http/Controllers/admin/Withdraw.php

@@ -52,6 +52,7 @@ class Withdraw extends Controller
                 'id' => ['nullable', 'integer', 'min:1'],
                 'type' => ['nullable', 'integer'],
                 'member_id' => ['nullable', 'string', 'min:1'],
+                'account' => ['nullable', 'string', 'max:64'],
                 'status' => ['nullable', 'integer'],
                 'first_name' => ['nullable'],
             ]);
@@ -60,7 +61,7 @@ class Withdraw extends Controller
             $params['type'] = $params['type'] ?? 2;
             
             $query = PaymentOrder::join('users', 'users.member_id', '=', 'payment_orders.member_id')
-                        ->select("payment_orders.*", "users.first_name","users.admin_note as user_admin_note");
+                        ->select("payment_orders.*", "users.first_name", "users.account", "users.admin_note as user_admin_note");
                         
             $where = PaymentOrderService::getWhere($params);
             $count = $query->where($where)->count();
@@ -121,13 +122,14 @@ class Withdraw extends Controller
                 'limit' => ['nullable', 'integer', 'min:1'],
                 'id' => ['nullable', 'integer', 'min:1'],
                 'member_id' => ['nullable', 'string', 'min:1'],
+                'account' => ['nullable', 'string', 'max:64'],
                 'status' => ['nullable', 'integer'],
                 'first_name' => ['nullable'],
             ]);
             $page = request()->input('page', 1);
             $limit = request()->input('limit', 10);
             $query = WithdrawModel::join('users', 'users.member_id', '=', 'withdraws.member_id')
-                        ->select("withdraws.*", "users.first_name","users.admin_note as user_admin_note");
+                        ->select("withdraws.*", "users.first_name", "users.account", "users.admin_note as user_admin_note");
             $where = WithdrawService::getWhere($params);
             $count = $query->where($where)->count();
             $list = $query->where($where)

+ 5 - 1
app/Http/Controllers/admin/YueBao.php

@@ -23,6 +23,7 @@ class YueBao extends Controller
                 'id' => ['nullable', 'integer', 'min:1'],
                 'user_id' => ['nullable'],
                 'member_id' => ['nullable'],
+                'account' => ['nullable', 'string', 'max:64'],
                 'first_name' => ['nullable'],
                 'start_time' => ['nullable', 'date', 'date_format:Y-m-d', 'required_with:end_time'],
                 'end_time' => ['nullable', 'date', 'date_format:Y-m-d', 'required_with:start_time'],
@@ -31,7 +32,7 @@ class YueBao extends Controller
             $limit = request()->input('limit', 15);
 
             $query = YuebaoItem::join('users', 'users.user_id', '=', 'yuebao_item.user_id')
-            ->select("yuebao_item.*", "users.first_name","users.member_id","users.admin_note as user_admin_note");
+            ->select("yuebao_item.*", "users.first_name", "users.member_id", "users.account", "users.admin_note as user_admin_note");
             if (!empty($params['id'])) {
                 $query = $query->where('yuebao_item.id', $params['id']);
             }
@@ -49,6 +50,9 @@ class YueBao extends Controller
                 $endTime = strtotime($params['end_time'] . " 23:59:59");
                 $query = $query->where('yuebao_item.create_time', '<=', $endTime);
             }
+            if (!empty($params['account'])) {
+                $query = $query->where('users.account', 'like', '%' . trim((string)$params['account']) . '%');
+            }
             if (isset($params['first_name']) && !empty($params['first_name'])) {
                 $query = $query->where('users.first_name', 'like', "%" . $params['first_name'] . "%");
             }

+ 1 - 1
app/Models/ActivityUser.php

@@ -39,7 +39,7 @@ class ActivityUser extends BaseModel
     public function member()
     {
         return $this->belongsTo(User::class, 'member_id', 'member_id')
-            ->select(['id', 'member_id', 'username', 'first_name', 'admin_note', 'status', 'phone', 'visitor_id', 'register_ip']);
+            ->select(['id', 'member_id', 'username', 'account', 'first_name', 'admin_note', 'status', 'phone', 'visitor_id', 'register_ip']);
     }
 
 

+ 1 - 1
app/Models/BalanceLog.php

@@ -11,7 +11,7 @@ class BalanceLog extends BaseModel
     function member()
     {
         return $this->belongsTo(User::class, 'member_id', 'member_id')
-            ->select(['id', 'member_id', 'username', 'first_name', 'admin_note', 'status', 'phone', 'visitor_id', 'register_ip']);
+            ->select(['id', 'member_id', 'username', 'account', 'first_name', 'admin_note', 'status', 'phone', 'visitor_id', 'register_ip']);
     }
 
     //用户累计消费总额

+ 1 - 1
app/Models/Rebate.php

@@ -28,7 +28,7 @@ class Rebate extends BaseModel
     public function member()
     {
         return $this->belongsTo(User::class, 'member_id', 'member_id')
-            ->select(['id', 'member_id', 'username', 'first_name', 'admin_note', 'status', 'phone', 'visitor_id', 'register_ip']);
+            ->select(['id', 'member_id', 'username', 'account', 'first_name', 'admin_note', 'status', 'phone', 'visitor_id', 'register_ip']);
     }
 
     protected function getGrantTimeAttribute($value): string

+ 1 - 1
app/Models/Recharge.php

@@ -41,7 +41,7 @@ class Recharge extends BaseModel
     public function member()
     {
         return $this->belongsTo(User::class, 'member_id', 'member_id')
-            ->select(['id', 'member_id', 'username', 'first_name', 'admin_note', 'status', 'phone', 'visitor_id', 'register_ip']);
+            ->select(['id', 'member_id', 'username', 'account', 'first_name', 'admin_note', 'status', 'phone', 'visitor_id', 'register_ip']);
     }
 
     protected function getAmountAttribute($value)

+ 1 - 1
app/Models/Withdraw.php

@@ -14,6 +14,6 @@ class Withdraw extends BaseModel
     public function member(): BelongsTo
     {
         return $this->belongsTo(User::class, 'member_id', 'member_id')
-            ->select(['id', 'member_id', 'username', 'first_name', 'admin_note', 'status', 'phone', 'visitor_id', 'register_ip']);
+            ->select(['id', 'member_id', 'username', 'account', 'first_name', 'admin_note', 'status', 'phone', 'visitor_id', 'register_ip']);
     }
 }

+ 4 - 1
app/Services/BackflowService.php

@@ -65,6 +65,9 @@ class BackflowService extends BaseService
             $where[] = ['backflow.status', '=', $search['status']];
         }
         
+        if (!empty($search['account'])) {
+            $where[] = ['users.account', 'like', '%' . trim((string)$search['account']) . '%'];
+        }
         if (!empty($search['first_name'])) {
             $where[] = ['users.first_name','like', '%'.$search['first_name'].'%'];
         }
@@ -80,7 +83,7 @@ class BackflowService extends BaseService
                 ->where(static::getWhere($search))
                 ->where('backflow.date', '<', $date);
 
-        $paginator = $query->select("backflow.*", "users.first_name","users.admin_note as user_admin_note")->orderByDesc('date')->orderBy('status')
+        $paginator = $query->select("backflow.*", "users.first_name", "users.account", "users.admin_note as user_admin_note")->orderByDesc('date')->orderBy('status')
             ->paginate($search['limit']);
 
 //        $count = $query->count();

+ 3 - 0
app/Services/BalanceLogService.php

@@ -101,6 +101,9 @@ class BalanceLogService extends BaseService
             $where[] = ['balance_logs.created_at', '>=', "{$search['start_time']} 00:00:00"];
             $where[] = ['balance_logs.created_at', '<=', "{$search['end_time']} 23:59:59"];
         }
+        if (!empty($search['account'])) {
+            $where[] = ['users.account', 'like', '%' . trim((string)$search['account']) . '%'];
+        }
         if (isset($search['first_name']) && !empty($search['first_name'])) {
             $where[] = ['users.first_name', 'like', "%" . $search['first_name'] . "%"];
         }

+ 7 - 1
app/Services/OnlineUserService.php

@@ -24,7 +24,7 @@ class OnlineUserService
     public const ONLINE_WINDOW_SECONDS = 1800;
 
     /**
-     * @param array{page?:int,limit?:int,member_id?:string,first_name?:string,login_ip?:string,platform?:string} $params
+     * @param array{page?:int,limit?:int,member_id?:string,account?:string,first_name?:string,login_ip?:string,platform?:string} $params
      * @return array{total:int,data:list<array>}
      */
     public static function list(array $params): array
@@ -54,6 +54,10 @@ class OnlineUserService
             $bindings[] = $params['member_id'];
             $bindings[] = $params['member_id'];
         }
+        if (!empty($params['account'])) {
+            $where[] = 'u.account LIKE ?';
+            $bindings[] = '%' . trim((string)$params['account']) . '%';
+        }
         if (!empty($params['first_name'])) {
             $where[] = 'u.first_name LIKE ?';
             $bindings[] = '%' . $params['first_name'] . '%';
@@ -128,6 +132,7 @@ class OnlineUserService
                 u.id,
                 u.member_id,
                 u.user_id,
+                u.account,
                 u.first_name,
                 u.last_active_time,
                 w.available_balance,
@@ -152,6 +157,7 @@ class OnlineUserService
             $row = (array)$row;
             return [
                 'member_id' => $row['member_id'] ?: $row['user_id'],
+                'account' => (string)($row['account'] ?? ''),
                 'first_name' => (string)($row['first_name'] ?? ''),
                 'available_balance' => (float)($row['available_balance'] ?? 0),
                 'login_ip' => (string)($row['login_ip'] ?? ''),

+ 4 - 1
app/Services/PaymentOrderService.php

@@ -91,6 +91,9 @@ class PaymentOrderService extends BaseService
         if (isset($search['status']) && $search['status'] != '') {
             $where[] = ['payment_orders.status', '=', $search['status']];
         }
+        if (!empty($search['account'])) {
+            $where[] = ['users.account', 'like', '%' . trim((string)$search['account']) . '%'];
+        }
         if (isset($search['first_name']) && !empty($search['first_name'])) {
             $where[] = ['users.first_name', 'like', "%" . $search['first_name'] . "%"];
         }
@@ -130,7 +133,7 @@ class PaymentOrderService extends BaseService
         $limit = isset($search['limit']) ? $search['limit'] : 15;
         $paginator = static::$MODEL::where(self::getWhere($search))
             ->join('users', 'users.member_id', '=', 'payment_orders.member_id')
-            ->select("payment_orders.*", "users.first_name","users.admin_note as user_admin_note") // 提前查好需要的字段
+            ->select("payment_orders.*", "users.first_name", "users.account", "users.admin_note as user_admin_note") // 提前查好需要的字段
             ->orderByDesc('payment_orders.created_at')
             ->paginate($limit);
 

+ 7 - 1
app/Services/RebateService.php

@@ -6,6 +6,7 @@ use App\Models\Rebate;
 use App\Models\Rebate as RebateModel;
 use Carbon\Carbon;
 use Illuminate\Database\Eloquent\Collection;
+use App\Support\MemberAccount;
 
 class RebateService extends BaseService
 {
@@ -79,12 +80,17 @@ class RebateService extends BaseService
         $date = Carbon::now('America/New_York')->format('Y-m-d');
         $paginator = static::$MODEL::where(self::getWhere($search))
             ->where('date', '<', $date)
+            ->tap(fn($query) => MemberAccount::filter($query, $search['account'] ?? null))
             ->with(['member'])
             ->orderByDesc('date')
             ->orderBy('status')
             ->orderByDesc('betting_amount')
             ->paginate($limit);
-        return ['total' => $paginator->total(), 'data' => $paginator->items()];
+        $list = $paginator->items();
+        foreach ($list as $item) {
+            $item['account'] = (string)($item->member->account ?? '');
+        }
+        return ['total' => $paginator->total(), 'data' => $list];
     }
 
 

+ 3 - 0
app/Services/RechargeService.php

@@ -18,6 +18,7 @@ use App\Services\BalanceLogService;
 use App\Helpers\TronHelper;
 use App\Models\Config;
 use App\Services\AdminRealtime\AdminRealtime;
+use App\Support\MemberAccount;
 
 /**
  * 用户充值记录
@@ -120,6 +121,7 @@ class RechargeService extends BaseService
         $first_name = $search['first_name'] ?? '';
         $paginator = static::$MODEL::where(self::getWhere($search))
             ->with(['member'])
+            ->tap(fn($query) => MemberAccount::filter($query, $search['account'] ?? null))
             ->when($first_name, function ($query) use ($first_name) {
                 $query->whereHas('member', function ($q) use ($first_name) {
                     $q->where('first_name', 'like', "%{$first_name}%");
@@ -133,6 +135,7 @@ class RechargeService extends BaseService
         $totalFail = 0;
         foreach ($list as $item) {
             $item['amount'] = floatval($item['amount']);
+            $item['account'] = (string)($item->member->account ?? '');
             $totalAmount += $item['amount'];
             if ($item['status'] == 1) $totalSuccess += $item['amount'];
             if ($item['status'] == 2) $totalFail += $item['amount'];

+ 5 - 0
app/Services/ThirdGameOrderService.php

@@ -209,6 +209,9 @@ class ThirdGameOrderService
                     });
             });
         }
+        if (!empty($params['account'])) {
+            $query->where('users.account', 'like', '%' . trim((string) $params['account']) . '%');
+        }
         if (!empty($params['first_name'])) {
             $firstName = '%' . $params['first_name'] . '%';
             $query->where(function ($query) use ($firstName) {
@@ -252,6 +255,7 @@ class ThirdGameOrderService
                 'users.id as current_user_id',
                 'users.member_id as current_member_id',
                 'users.username as current_username',
+                'users.account as current_account',
                 'users.first_name as current_first_name',
             ])
             ->orderByDesc('third_game_orders.last_update_time')
@@ -446,6 +450,7 @@ class ThirdGameOrderService
             'user_id' => $userId === null ? null : (int) $userId,
             'member_id' => $memberId === null ? null : (string) $memberId,
             'username' => (string) ($username ?? ''),
+            'account' => (string) ($order->current_account ?? ''),
             'first_name' => (string) ($firstName ?? ''),
             'player_id' => (string) $order->player_id,
             'platform' => (string) $order->platform,

+ 5 - 0
app/Services/ThirdPartyDepositService.php

@@ -113,6 +113,7 @@ class ThirdPartyDepositService
                 'payment_orders.created_at as submitted_at',
                 'users.user_id',
                 'users.username',
+                'users.account',
                 'users.first_name',
             ]);
 
@@ -174,6 +175,9 @@ class ThirdPartyDepositService
         if (!empty($params['member_id'])) {
             $query->where('payment_orders.member_id', (string)$params['member_id']);
         }
+        if (!empty($params['account'])) {
+            $query->where('users.account', 'like', '%' . trim((string)$params['account']) . '%');
+        }
         if (!empty($params['payment_company'])) {
             self::filterPaymentCompany($query, (string)$params['payment_company']);
         }
@@ -249,6 +253,7 @@ class ThirdPartyDepositService
             'id' => (int)$row->id,
             'member_id' => (string)$row->member_id,
             'username' => (string)($row->username ?? ''),
+            'account' => (string)($row->account ?? ''),
             'first_name' => (string)($row->first_name ?? ''),
             'payment_company' => $company['code'],
             'payment_company_name' => $company['name'],

+ 3 - 0
app/Services/WithdrawService.php

@@ -137,6 +137,9 @@ class WithdrawService
         if (isset($search['status']) && $search['status'] != '') {
             $where[] = ['withdraws.status', '=', $search['status']];
         }
+        if (!empty($search['account'])) {
+            $where[] = ['users.account', 'like', '%' . trim((string)$search['account']) . '%'];
+        }
         if (isset($search['first_name']) && !empty($search['first_name'])) {
             $where[] = ['users.first_name', 'like', "%" . $search['first_name'] . "%"];
         }

+ 21 - 0
app/Support/MemberAccount.php

@@ -0,0 +1,21 @@
+<?php
+
+namespace App\Support;
+
+use App\Models\User;
+
+/**
+ * 后台列表按会员账号(users.account)模糊筛选,适用于未 join users 的查询。
+ */
+class MemberAccount
+{
+    /** @param string $column 当前查询里的会员ID列 */
+    public static function filter($query, $account, string $column = 'member_id')
+    {
+        $account = trim((string)$account);
+        if ($account !== '') {
+            $query->whereIn($column, User::query()->select('member_id')->where('account', 'like', '%' . $account . '%'));
+        }
+        return $query;
+    }
+}

+ 2 - 0
docs/admin-finance-api.md

@@ -150,6 +150,7 @@ POST 请求使用 `Content-Type: application/json`。统一响应:`code = 0` 
 | --- | --- | --- |
 | `page` / `limit` | 否 | 默认 `1/20`,`limit` 最大 `200` |
 | `member_id` | 否 | 会员 ID |
+| `account` | 否 | 会员账号,模糊匹配 |
 | `payment_company` | 否 | `sanjin`、`jd`、`no`、`zimu` |
 | `payment_method` | 否 | 支付方式,使用返回的选项值 |
 | `channel` | 否 | 通道编号 |
@@ -170,6 +171,7 @@ POST 请求使用 `Content-Type: application/json`。统一响应:`code = 0` 
         "id": 1,
         "member_id": "10001",
         "username": "test01",
+        "account": "tg12345678",
         "first_name": "test01",
         "payment_company": "zimu",
         "payment_company_name": "808支付",

+ 2 - 0
docs/admin-online-register-api.md

@@ -54,6 +54,7 @@ GET /admin/online/users
 | `page` | int | 否 | `1` | 页码,≥1 |
 | `limit` | int | 否 | `15` | 每页条数,1~200 |
 | `member_id` | string | 否 | - | 会员 ID(匹配 `member_id` 或 `user_id`) |
+| `account` | string | 否 | - | 会员账号,模糊匹配 |
 | `first_name` | string | 否 | - | 会员昵称,模糊匹配 |
 | `login_ip` | string | 否 | - | IP,模糊匹配最近登录 IP |
 | `platform` | string | 否 | - | 平台:`PC` / `Android` / `iOS` / `H5` / `WeChat`;空或「全部」表示不限 |
@@ -76,6 +77,7 @@ GET /admin/online/users?page=1&limit=15&member_id=&first_name=&login_ip=&platfor
 | 字段 | 类型 | 前端列名 | 说明 |
 |------|------|----------|------|
 | `member_id` | string | 会员ID | 优先 `member_id`,否则 `user_id` |
+| `account` | string | 会员账号 | 未设置时为空字符串 |
 | `first_name` | string | 会员昵称 | |
 | `available_balance` | number | 账户余额 | 主钱包可用余额 |
 | `login_ip` | string | IP地址 | 最近一次成功登录 IP |

+ 2 - 0
docs/admin-third-game-order-api.md

@@ -18,6 +18,7 @@ Authorization: Bearer <token>
 | `user_id` | 否 | 系统数据库用户主键,精确匹配 |
 | `member_id` | 否 | 系统会员 ID,精确匹配 |
 | `username` | 否 | 系统用户名,模糊匹配 |
+| `account` | 否 | 会员账号,模糊匹配 |
 | `first_name` | 否 | 系统昵称,模糊匹配 |
 | `player_id` | 否 | 三方玩家 ID,精确匹配 |
 | `platform` | 否 | 三方游戏平台,如 `ag`、`pg` |
@@ -41,6 +42,7 @@ Authorization: Bearer <token>
         "user_id": 123,
         "member_id": "10001",
         "username": "test01",
+        "account": "tg12345678",
         "first_name": "测试用户",
         "player_id": "u0123",
         "platform": "ag",

+ 101 - 0
tests/Integration/AdminListAccountTest.php

@@ -0,0 +1,101 @@
+<?php
+
+namespace Tests\Integration;
+
+use App\Services\BackflowService;
+use App\Services\RebateService;
+use App\Services\RechargeService;
+use Illuminate\Config\Repository;
+use Illuminate\Database\Capsule\Manager;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Foundation\Application;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Facade;
+use PHPUnit\Framework\TestCase;
+
+/** 后台列表返回会员账号 account,并支持按 account 模糊筛选(关联查询与 join 查询两种写法)。 */
+class AdminListAccountTest extends TestCase
+{
+    protected function setUp(): void
+    {
+        parent::setUp();
+        Facade::clearResolvedInstances();
+        $app = new Application(dirname(__DIR__, 2));
+        $app->instance('config', new Repository(['app' => ['timezone' => 'Asia/Shanghai', 'url' => 'http://x']]));
+        Facade::setFacadeApplication($app);
+        $db = new Manager($app);
+        $db->addConnection(['driver' => 'sqlite', 'database' => ':memory:', 'prefix' => 'bot_']);
+        $db->setAsGlobal();
+        $db->bootEloquent();
+        $app->instance('db', $db->getDatabaseManager());
+        $app->bind('db.schema', fn () => $db->schema());
+        $schema = $db->schema();
+        $schema->create('users', function (Blueprint $t) {
+            $t->id(); $t->string('member_id'); $t->string('account')->nullable(); $t->string('username')->nullable();
+            $t->string('first_name')->nullable(); $t->string('admin_note')->nullable(); $t->integer('status')->default(0);
+            $t->string('phone')->nullable(); $t->string('visitor_id')->nullable(); $t->string('register_ip')->nullable(); $t->timestamps();
+        });
+        $schema->create('rebates', function (Blueprint $t) {
+            $t->id(); $t->string('member_id'); $t->date('date'); $t->integer('status')->default(0);
+            $t->decimal('betting_amount', 20, 2)->default(0); $t->timestamps();
+        });
+        $schema->create('backflow', function (Blueprint $t) {
+            $t->id(); $t->string('member_id'); $t->string('date'); $t->integer('status')->default(0); $t->timestamps();
+        });
+        $schema->create('recharges', function (Blueprint $t) {
+            $t->id(); $t->string('member_id'); $t->string('coin')->default('CNY'); $t->decimal('amount', 20, 2)->default(0);
+            $t->integer('status')->default(0); $t->timestamps();
+        });
+        DB::table('users')->insert([
+            ['member_id' => 'm1', 'account' => 'alice'],
+            ['member_id' => 'm2', 'account' => 'bob'],
+            ['member_id' => 'm3', 'account' => null],
+        ]);
+    }
+
+    protected function tearDown(): void
+    {
+        DB::disconnect();
+        Facade::clearResolvedInstances();
+        parent::tearDown();
+    }
+
+    public function test_rebate_list_shows_and_filters_account(): void
+    {
+        foreach (['m1', 'm2', 'm3'] as $m) {
+            DB::table('rebates')->insert(['member_id' => $m, 'date' => '2026-01-01']);
+        }
+        $rows = $this->accounts(RebateService::paginate(['limit' => 10])['data']);
+        $this->assertSame(['m1' => 'alice', 'm2' => 'bob', 'm3' => ''], $rows);
+        $this->assertSame(['m1' => 'alice'], $this->accounts(RebateService::paginate(['account' => 'lic'])['data']));
+        $this->assertSame(0, RebateService::paginate(['account' => 'nobody'])['total']);
+    }
+
+    public function test_backflow_list_shows_and_filters_account(): void
+    {
+        foreach (['m1', 'm2'] as $m) {
+            DB::table('backflow')->insert(['member_id' => $m, 'date' => '2026-01']);
+        }
+        $this->assertSame(['m1' => 'alice', 'm2' => 'bob'], $this->accounts(BackflowService::paginate(['limit' => 10])['data']));
+        $this->assertSame(['m2' => 'bob'], $this->accounts(BackflowService::paginate(['limit' => 10, 'account' => 'bo'])['data']));
+    }
+
+    public function test_recharge_list_shows_and_filters_account(): void
+    {
+        foreach (['m1', 'm2'] as $m) {
+            DB::table('recharges')->insert(['member_id' => $m, 'amount' => 1, 'created_at' => '2026-01-01 00:00:00']);
+        }
+        $this->assertSame(['m1' => 'alice', 'm2' => 'bob'], $this->accounts(RechargeService::paginate([])['data']));
+        $this->assertSame(['m1' => 'alice'], $this->accounts(RechargeService::paginate(['account' => 'ALI'])['data']));
+    }
+
+    private function accounts(array $rows): array
+    {
+        $out = [];
+        foreach (json_decode(json_encode($rows), true) as $row) {
+            $out[$row['member_id']] = $row['account'];
+        }
+        ksort($out);
+        return $out;
+    }
+}

+ 1 - 1
tests/Integration/AdminRealtimeTest.php

@@ -77,7 +77,7 @@ class AdminRealtimeTest extends TestCase
         (require dirname(__DIR__, 2) . '/database/migrations/2026_10_31_210000_add_seq_to_admin_realtime_events.php')->up();
         (require dirname(__DIR__, 2) . '/database/migrations/2026_11_01_000000_add_delivery_state_to_admin_realtime_events.php')->up();
         Schema::create('users', function (Blueprint $t) {
-            $t->id(); $t->string('member_id'); $t->string('user_id')->default(''); $t->string('first_name')->default('');
+            $t->id(); $t->string('member_id'); $t->string('user_id')->default(''); $t->string('account')->nullable(); $t->string('first_name')->default('');
             $t->string('admin_note')->nullable(); $t->string('language')->nullable(); $t->integer('recharge_channel_group_id')->nullable(); $t->timestamps();
         });
         Schema::create('withdraws', function (Blueprint $t) {

+ 1 - 1
tests/Integration/SharingSettlementTest.php

@@ -33,7 +33,7 @@ class SharingSettlementTest extends TestCase
         $db->setAsGlobal(); $db->bootEloquent(); $app->instance('db',$db->getDatabaseManager()); $app->bind('db.schema',fn()=>$db->schema());
         $db->schema()->create('users',function(Blueprint $t){
             $t->id(); $t->string('member_id'); $t->string('user_code'); $t->string('agent_user_code')->nullable(); $t->integer('from')->default(1); $t->timestamps();
-            $t->string('user_id')->nullable(); $t->string('first_name')->nullable(); $t->string('username')->nullable(); $t->string('language')->nullable();
+            $t->string('user_id')->nullable(); $t->string('first_name')->nullable(); $t->string('username')->nullable(); $t->string('account')->nullable(); $t->string('language')->nullable();
             $t->integer('recharge_channel_group_id')->default(0);
         });
         $db->schema()->create('share_settings',function(Blueprint $t){

+ 1 - 1
tests/Integration/ThirdGamePlayerIdentityTest.php

@@ -23,7 +23,7 @@ class ThirdGamePlayerIdentityTest extends TestCase
         $db = new Manager($app);
         $db->addConnection(['driver' => 'sqlite', 'database' => ':memory:', 'prefix' => 'bot_']);
         $db->setAsGlobal(); $db->bootEloquent(); $app->instance('db', $db->getDatabaseManager());
-        DB::statement('CREATE TABLE bot_users (id INTEGER PRIMARY KEY,member_id TEXT,username TEXT,first_name TEXT)');
+        DB::statement('CREATE TABLE bot_users (id INTEGER PRIMARY KEY,member_id TEXT,username TEXT,account TEXT,first_name TEXT)');
         DB::statement('CREATE TABLE bot_third_game_orders (id INTEGER PRIMARY KEY AUTOINCREMENT,order_key TEXT UNIQUE,
             user_id INTEGER,member_id TEXT,username TEXT,first_name TEXT,player_id TEXT,platform TEXT,currency TEXT,
             game_type INTEGER,game_name TEXT,round_no TEXT,table_no TEXT,seat TEXT,bet_amount TEXT,valid_amount TEXT,