| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117 |
- <?php
- namespace App\Services\Simulation;
- use App\Constants\HttpStatus;
- use App\Models\User;
- use App\Models\UserLogin;
- use App\Models\Wallet;
- use App\Support\MemberAudience;
- use Exception;
- use Illuminate\Support\Facades\DB;
- use Illuminate\Support\Facades\Schema;
- /** 后台模拟会员:users.user_type = 2、from = 2、没有代理和上级,不能登录前台。 */
- final class SimulationMemberService
- {
- /** @return list<array{id:int,member_id:string,account:string}> */
- public static function create(int $count, array $options = []): array
- {
- SimulationSettings::assertEnabled();
- $settings = SimulationSettings::all();
- if ($count < 1 || $count > (int) $settings['simulation_max_members_per_task']) {
- throw new Exception('一次最多创建 ' . $settings['simulation_max_members_per_task'] . ' 个模拟会员', HttpStatus::CUSTOM_ERROR);
- }
- $existing = DB::table('users')->where('user_type', MemberAudience::TYPE_SIMULATED)->count();
- if ($existing + $count > (int) $settings['simulation_max_members_total']) {
- throw new Exception('模拟会员总数将超过上限 ' . $settings['simulation_max_members_total'], HttpStatus::CUSTOM_ERROR);
- }
- $domain = trim((string) ($options['domain'] ?? '')) ?: $settings['simulation_default_domain'];
- $initial = $options['initial_balance'] ?? $settings['simulation_initial_balance'];
- $created = [];
- for ($i = 0; $i < $count; $i++) {
- $user = DB::transaction(function () use ($domain) {
- $sn = self::serial();
- $user = (new User())->forceFill([
- 'member_id' => $sn, 'user_id' => $sn, 'account' => 'sim_' . $sn, 'first_name' => '模拟_' . substr($sn, -4),
- 'username' => '', 'from' => '2', 'user_type' => MemberAudience::TYPE_SIMULATED,
- 'register_ip' => '127.0.0.1', 'register_domain' => $domain, 'last_active_time' => time(),
- ]);
- $user->save();
- (new Wallet())->forceFill(['user_id' => $user->id, 'member_id' => $sn, 'coin' => 'USDT', 'net' => 'TRC20', 'available_balance' => 0])->save();
- return $user;
- });
- MemberAudience::remember($user->member_id);
- if (is_numeric($initial) && $initial > 0) SimulationFundService::adjust($user->member_id, $initial, 'credit', '模拟会员初始余额');
- $created[] = ['id' => (int) $user->id, 'member_id' => (string) $user->member_id, 'account' => (string) $user->account];
- }
- return $created;
- }
- /** 8 位 1-9 数字,与前台会员编号同形;纯数字是因为 wallets.member_id 是 bigint。 */
- private static function serial(): string
- {
- for ($try = 0; $try < 50; $try++) {
- $sn = '';
- for ($i = 0; $i < 8; $i++) $sn .= (string) random_int(1, 9);
- if (!DB::table('users')->where('member_id', $sn)->orWhere('user_id', $sn)->exists()) return $sn;
- }
- throw new Exception('会员编号生成失败,请重试', HttpStatus::CUSTOM_ERROR);
- }
- public static function setBanned($memberId, int $banned): void
- {
- $user = SimulationSettings::member($memberId);
- DB::table('users')->where('id', $user->id)->update(['is_banned' => $banned ? 1 : 0]);
- }
- /** 只写登录日志,不建会话、不签发 token:模拟会员没有可登录前台的凭证。 */
- public static function login($memberId, string $platform = 'H5', ?string $domain = null): array
- {
- SimulationSettings::assertEnabled();
- $user = SimulationSettings::member($memberId);
- self::logout($memberId);
- $row = ['user_id' => (string) $user->user_id, 'login_account' => (string) $user->account, 'login_ip' => '127.0.0.1',
- 'login_domain' => $domain ?: SimulationSettings::get('simulation_default_domain'), 'platform' => $platform, 'status' => UserLogin::STATUS_SUCCESS,
- 'created_at' => now(), 'updated_at' => now()];
- $row = array_filter($row, fn ($column) => Schema::hasColumn('user_login', $column), ARRAY_FILTER_USE_KEY);
- $id = DB::table('user_login')->insertGetId($row);
- $update = ['last_active_time' => time()];
- if (Schema::hasColumn('users', 'last_login_time')) $update += ['last_login_time' => time(), 'login_count' => DB::raw('COALESCE(login_count, 0) + 1')];
- DB::table('users')->where('id', $user->id)->update($update);
- return ['id' => $id, 'member_id' => (string) $user->member_id];
- }
- public static function logout($memberId): int
- {
- $user = SimulationSettings::member($memberId);
- if (!Schema::hasColumn('user_login', 'logout_time')) return 0;
- $open = DB::table('user_login')->where('user_id', (string) $user->user_id)->where('status', UserLogin::STATUS_SUCCESS)->whereNull('logout_time')->get(['id', 'created_at']);
- foreach ($open as $row) {
- DB::table('user_login')->where('id', $row->id)->update(['logout_time' => now(),
- 'online_duration' => max(0, now()->getTimestamp() - strtotime((string) $row->created_at)), 'updated_at' => now()]);
- }
- return $open->count();
- }
- /** 当前“在线”的模拟会员:有未登出的成功登录记录。 */
- public static function onlineUserIds()
- {
- $query = DB::table('user_login')->where('status', UserLogin::STATUS_SUCCESS)->whereIn('user_id', MemberAudience::simulatedMembers('user_id'));
- if (Schema::hasColumn('user_login', 'logout_time')) $query->whereNull('logout_time');
- return $query->distinct()->pluck('user_id');
- }
- public static function detail($memberId): array
- {
- $user = SimulationSettings::member($memberId);
- $wallet = DB::table('wallets')->where('member_id', $user->member_id)->first();
- return [
- 'user' => ['id' => $user->id, 'member_id' => $user->member_id, 'account' => $user->account, 'first_name' => $user->first_name,
- 'is_banned' => (int) ($user->is_banned ?? 0), 'register_domain' => $user->register_domain ?? '', 'created_at' => $user->created_at],
- 'wallet' => $wallet ? ['available_balance' => (string) $wallet->available_balance, 'frozen_balance' => (string) $wallet->frozen_balance,
- 'wager_required' => (string) ($wallet->wager_required ?? 0), 'wager_completed' => (string) ($wallet->wager_completed ?? 0)] : null,
- 'logs' => DB::table('balance_logs')->where('member_id', $user->member_id)->orderByDesc('id')->limit(20)->get(),
- ];
- }
- }
|