instance('config', new Repository(['app' => ['timezone' => 'Asia/Shanghai']])); 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()); $db->schema()->create('users', function (Blueprint $t) { $t->id(); $t->string('member_id'); $t->string('account')->nullable(); $t->timestamps(); }); $db->schema()->create('manual_audits', function (Blueprint $t) { $t->id(); $t->string('request_id'); $t->string('request_hash'); $t->integer('admin_id')->nullable(); $t->integer('balance_log_id')->nullable(); $t->string('member_id'); $t->string('username')->default(''); $t->string('first_name')->default(''); $t->string('transaction_type'); $t->string('change_type'); $t->decimal('amount', 30, 10); $t->decimal('before_balance', 30, 10)->nullable(); $t->decimal('after_balance', 30, 10)->nullable(); $t->integer('level_before')->default(0); $t->string('level_before_name')->default(''); $t->integer('level_after')->default(0); $t->string('level_after_name')->default(''); $t->integer('status')->default(0); $t->string('remark')->default(''); $t->string('failure_reason')->default(''); $t->timestamps(); }); DB::table('users')->insert([ ['member_id' => 'm1', 'account' => 'alice'], ['member_id' => 'm2', 'account' => 'dup'], ['member_id' => 'm3', 'account' => 'dup'], ]); } protected function tearDown(): void { DB::disconnect(); Facade::clearResolvedInstances(); parent::tearDown(); } public function test_member_id_and_account_resolve_to_one_member(): void { $this->assertSame('m1', ManualAuditService::resolveMemberId('m1', null)); $this->assertSame('m1', ManualAuditService::resolveMemberId(null, 'alice')); $this->assertSame('m1', ManualAuditService::resolveMemberId(' m1 ', ' alice ')); $this->assertResolveFails('m2', 'alice', '会员ID与账号不一致'); $this->assertResolveFails(null, 'nobody', '账号不存在'); $this->assertResolveFails(null, 'dup', '对应多个会员'); $this->assertResolveFails('', '', '请填写会员ID或账号'); } public function test_list_and_detail_return_the_member_account(): void { foreach (['m1', 'm9'] as $i => $memberId) { DB::table('manual_audits')->insert(['request_id' => "r{$i}", 'request_hash' => 'h', 'member_id' => $memberId, 'transaction_type' => 'credit', 'change_type' => '人工充值', 'amount' => 10, 'status' => 1, 'created_at' => '2026-11-04 12:00:00']); } $rows = array_column(ManualAuditService::list([])['data'], 'account', 'member_id'); $this->assertSame(['m9' => '', 'm1' => 'alice'], $rows); $this->assertSame('alice', ManualAuditService::detail(1)['account']); } private function assertResolveFails(?string $memberId, ?string $account, string $message): void { try { ManualAuditService::resolveMemberId($memberId, $account); $this->fail('Expected failure: ' . $message); } catch (\Exception $e) { $this->assertStringContainsString($message, $e->getMessage()); } } }