AdminBindSuperiorTest.php 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. <?php
  2. namespace Tests\Integration;
  3. use App\Http\Controllers\admin\User as AdminUser;
  4. use App\Models\Agent\Agent;
  5. use App\Services\MemberSuperiorService;
  6. use Illuminate\Database\Schema\Blueprint;
  7. use Illuminate\Http\Request;
  8. use Illuminate\Support\Facades\DB;
  9. use Illuminate\Support\Facades\Schema;
  10. use PHPUnit\Framework\TestCase;
  11. use Tests\Support\AgentTestEnvironment;
  12. /** 后台会员列表:无上级的会员可按账号补绑合营代理,或按会员 ID 补绑上级会员。 */
  13. class AdminBindSuperiorTest extends TestCase
  14. {
  15. use AgentTestEnvironment { setUp as private agentSetUp; }
  16. protected function setUp(): void
  17. {
  18. $this->agentSetUp();
  19. Schema::table('users', function (Blueprint $t) {
  20. $t->integer('from')->default(1); $t->string('account')->default('');
  21. $t->string('user_code')->nullable(); $t->string('agent_user_code')->nullable();
  22. });
  23. Schema::create('share_members', function (Blueprint $t) {
  24. $t->unsignedBigInteger('user_id')->primary(); $t->unsignedBigInteger('parent_user_id')->nullable();
  25. $t->string('invitation_code', 100)->default(''); $t->string('binding_status', 24)->default('root');
  26. $t->unsignedInteger('level')->default(1); $t->dateTime('bound_at'); $t->timestamps();
  27. });
  28. Schema::create('share_tree', function (Blueprint $t) {
  29. $t->unsignedBigInteger('ancestor_user_id'); $t->unsignedBigInteger('descendant_user_id');
  30. $t->unsignedInteger('depth'); $t->dateTime('bound_at')->default('1970-01-01 00:00:00');
  31. $t->primary(['ancestor_user_id', 'descendant_user_id']);
  32. });
  33. Schema::create('share_accounts', function (Blueprint $t) {
  34. $t->unsignedBigInteger('user_id')->primary(); $t->timestamps();
  35. });
  36. DB::table('share_system_state')->insert(['key' => 'relations_lock', 'value' => '1']);
  37. }
  38. private function member(string $memberId, array $extra = []): int
  39. {
  40. $id = DB::table('users')->insertGetId($extra + ['member_id' => $memberId, 'username' => $memberId, 'account' => $memberId,
  41. 'user_code' => 'C' . $memberId]);
  42. if (($extra['from'] ?? 1) !== 2) {
  43. DB::table('share_members')->insert(['user_id' => $id, 'invitation_code' => 'C' . $memberId, 'bound_at' => '2026-09-01 00:00:00']);
  44. DB::table('share_tree')->insert(['ancestor_user_id' => $id, 'descendant_user_id' => $id, 'depth' => 0]);
  45. }
  46. return $id;
  47. }
  48. private function call(array $params): array
  49. {
  50. $request = Request::create('/admin/user/bindSuperior', 'POST', $params);
  51. $request->user = (object) ['id' => 1];
  52. $this->app->instance('request', $request);
  53. return (new AdminUser())->bindSuperior(new MemberSuperiorService())->getData(true);
  54. }
  55. public function test_binds_agent_or_member_only_when_no_superior(): void
  56. {
  57. $agent = $this->agent();
  58. $this->member('1001');
  59. $b = $this->member('1002');
  60. $c = $this->member('1003');
  61. $this->member('1004', ['from' => 2]);
  62. $data = $this->call(['member_id' => '1001', 'type' => 'agent', 'agent_username' => ' ' . $agent->username . ' ']);
  63. $this->assertSame(0, $data['code'], (string) $data['msg']);
  64. $this->assertSame($agent->id, $data['data']['agent_id']);
  65. $this->assertSame($agent->id, (int) DB::table('users')->where('member_id', '1001')->value('agent_id'));
  66. $this->assertSame('会员已有上级,不能修改', $this->call(['member_id' => '1001', 'type' => 'member', 'superior_id' => '1002'])['msg']);
  67. $data = $this->call(['member_id' => '1003', 'type' => 'member', 'superior_id' => '1002']);
  68. $this->assertSame(0, $data['code'], (string) $data['msg']);
  69. $this->assertSame('C1002', DB::table('users')->where('id', $c)->value('agent_user_code'));
  70. $this->assertSame($b, (int) DB::table('share_members')->where('user_id', $c)->value('parent_user_id'));
  71. $this->assertTrue(DB::table('share_tree')->where(['ancestor_user_id' => $b, 'descendant_user_id' => $c, 'depth' => 1])->exists());
  72. $this->assertSame('会员已有上级,不能修改', $this->call(['member_id' => '1003', 'type' => 'agent', 'agent_username' => $agent->username])['msg']);
  73. $this->assertSame('不能绑定自己或自己的下级', $this->call(['member_id' => '1002', 'type' => 'member', 'superior_id' => '1003'])['msg'], 'no cycle');
  74. $this->assertSame('不能绑定自己', $this->call(['member_id' => '1002', 'type' => 'member', 'superior_id' => '1002'])['msg']);
  75. $this->assertSame('上级会员不存在', $this->call(['member_id' => '1002', 'type' => 'member', 'superior_id' => '9999'])['msg']);
  76. $this->assertSame('代理不存在', $this->call(['member_id' => '1002', 'type' => 'agent', 'agent_username' => 'unknown-agent'])['msg']);
  77. $this->assertSame('试玩会员不能作为上级', $this->call(['member_id' => '1002', 'type' => 'member', 'superior_id' => '1004'])['msg']);
  78. $this->assertSame('试玩会员不能绑定上级', $this->call(['member_id' => '1004', 'type' => 'member', 'superior_id' => '1002'])['msg']);
  79. $this->assertNotSame(0, $this->call(['member_id' => '1002', 'type' => 'other', 'superior_id' => '1'])['code']);
  80. $equity = $this->agent(Agent::TYPE_EQUITY);
  81. $this->assertSame('只能绑定合营代理', $this->call(['member_id' => '1002', 'type' => 'agent', 'agent_username' => $equity->username])['msg']);
  82. $this->member('1005', ['agent_id' => $equity->id]);
  83. $this->member('1006', ['agent_id' => $equity->id, 'agent_user_code' => 'GONE']);
  84. $data = $this->call(['member_id' => '1005', 'type' => 'agent', 'agent_username' => $agent->username]);
  85. $this->assertSame(0, $data['code'], (string) $data['msg']);
  86. $this->assertSame($agent->id, (int) DB::table('users')->where('member_id', '1005')->value('agent_id'));
  87. $data = $this->call(['member_id' => '1006', 'type' => 'member', 'superior_id' => '1002']);
  88. $this->assertSame(0, $data['code'], (string) $data['msg']);
  89. $this->assertSame('C1002', DB::table('users')->where('member_id', '1006')->value('agent_user_code'));
  90. $this->assertNull(DB::table('users')->where('member_id', '1006')->value('agent_id'));
  91. Agent::query()->whereKey($agent->id)->update(['status' => Agent::STATUS_DISABLED]);
  92. $this->assertSame('代理已禁用', $this->call(['member_id' => '1002', 'type' => 'agent', 'agent_username' => $agent->username])['msg']);
  93. $this->assertNull(DB::table('users')->where('id', $b)->value('agent_id'));
  94. }
  95. public function test_rejects_mixed_target_fields_and_never_falls_back_to_other_identifiers(): void
  96. {
  97. $agent = $this->agent();
  98. $member = $this->member('1001');
  99. $this->member('1002', ['account' => 'inviter-account']);
  100. foreach ([
  101. ['type' => 'agent', 'superior_id' => (string) $agent->id],
  102. ['type' => 'agent', 'agent_username' => $agent->username, 'superior_id' => '1002'],
  103. ['type' => 'member', 'agent_username' => $agent->username],
  104. ['type' => 'member', 'superior_id' => '1002', 'agent_username' => $agent->username],
  105. ] as $params) {
  106. $this->assertNotSame(0, $this->call($params + ['member_id' => '1001'])['code']);
  107. }
  108. $this->assertSame('代理不存在', $this->call([
  109. 'member_id' => '1001', 'type' => 'agent', 'agent_username' => (string) $agent->id,
  110. ])['msg']);
  111. $this->assertSame('上级会员不存在', $this->call([
  112. 'member_id' => '1001', 'type' => 'member', 'superior_id' => 'inviter-account',
  113. ])['msg']);
  114. $this->assertNull(DB::table('users')->where('id', $member)->value('agent_id'));
  115. $this->assertNull(DB::table('share_members')->where('user_id', $member)->value('parent_user_id'));
  116. }
  117. }