StatsScope.php 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. <?php
  2. namespace App\Support;
  3. use Illuminate\Support\Facades\DB;
  4. /**
  5. * 统计口径:排除游客(users.from = 2)、后台模拟会员(users.user_type = 2)和测试会员 / 测试代理(agents.is_test = 1)。
  6. * 模拟口径(MemberAudience::simulated())下反过来:只统计模拟会员,代理资金一律不计。
  7. * 测试会员 = users.agent_id 指向测试代理的会员,按代理当前设置判断(切换后历史数据跟着变)。
  8. * 只用于统计;明细列表、待办角标、代理看自己名下会员的数据不用它。
  9. */
  10. final class StatsScope
  11. {
  12. private static ?\WeakMap $enabled = null;
  13. /** agents.is_test 存在时才过滤(迁移执行前、或没有代理表时不过滤)。按数据库连接缓存,避免每次查表结构。 */
  14. public static function enabled(): bool
  15. {
  16. self::$enabled ??= new \WeakMap();
  17. $connection = DB::connection();
  18. return self::$enabled[$connection] ??= DB::getSchemaBuilder()->hasTable('agents') && DB::getSchemaBuilder()->hasColumn('agents', 'is_test');
  19. }
  20. /** 当前所有测试代理的 ID(用于在 PHP 里过滤已加载的数据)。 */
  21. public static function testAgentIdList(): array
  22. {
  23. return self::enabled() ? array_map('intval', self::testAgentIds()->pluck('id')->all()) : [];
  24. }
  25. /** 测试代理 ID 子查询。 */
  26. public static function testAgentIds()
  27. {
  28. return DB::table('agents')->select('id')->where('is_test', 1);
  29. }
  30. /** 测试会员的某一列(member_id / id)子查询。 */
  31. public static function testMembers(string $column = 'member_id')
  32. {
  33. return DB::table('users')->select($column)->whereNotNull($column)->whereIn('agent_id', self::testAgentIds());
  34. }
  35. /** 不进正式统计的会员的某一列子查询:模拟会员、游客(from = 2)、测试会员。 */
  36. private static function excludedMembers(string $column)
  37. {
  38. $testAgents = self::enabled();
  39. return DB::table('users')->select($column)->whereNotNull($column)->where(function ($q) use ($testAgents) {
  40. $q->where('user_type', '<>', 0)->orWhere('from', 2);
  41. if ($testAgents) $q->orWhereIn('agent_id', self::testAgentIds());
  42. });
  43. }
  44. /** users 查询:排除测试会员和模拟会员(游客保留,由调用方按 from 区分)。$alias 为 users 表在查询里的别名或表名。 */
  45. public static function excludeTestUsers($query, string $alias = 'users')
  46. {
  47. MemberAudience::visibleUsers($query, $alias);
  48. if (MemberAudience::isSimulated() || !self::enabled()) return $query;
  49. return $query->where(fn ($q) => $q->whereNull($alias . '.agent_id')->orWhereNotIn($alias . '.agent_id', self::testAgentIds()));
  50. }
  51. /** users 查询:只留真实会员(排除游客、模拟会员和测试会员)。模拟口径下只留模拟会员。 */
  52. public static function realUsers($query, string $alias = 'users')
  53. {
  54. if (MemberAudience::isSimulated()) return MemberAudience::visibleUsers($query, $alias);
  55. $query->where(fn ($q) => $q->whereNull($alias . '.from')->orWhere($alias . '.from', '<>', 2));
  56. return self::excludeTestUsers($query, $alias);
  57. }
  58. /** users 查询:只留游客(from = 2 且不是模拟会员)。 */
  59. public static function guestUsers($query, string $alias = 'users')
  60. {
  61. if (MemberAudience::isSimulated()) return $query->whereRaw('1 = 0');
  62. return MemberAudience::visibleUsers($query->where($alias . '.from', 2), $alias);
  63. }
  64. /** 原生 SQL 用:users 别名为 $alias 时追加的条件(以 AND 开头),只留真实会员;模拟口径下只留模拟会员。 */
  65. public static function rawExcludeTestUsers(string $alias = 'u'): string
  66. {
  67. if (MemberAudience::isSimulated()) return MemberAudience::rawVisibleUsers($alias);
  68. $sql = MemberAudience::enabled() ? " AND {$alias}.user_type = 0 AND ({$alias}.`from` IS NULL OR {$alias}.`from` <> '2')" : '';
  69. if (!self::enabled()) return $sql;
  70. $agents = DB::getTablePrefix() . 'agents';
  71. return $sql . " AND ({$alias}.agent_id IS NULL OR {$alias}.agent_id NOT IN (SELECT id FROM {$agents} WHERE is_test = 1))";
  72. }
  73. /** 按会员统计的表(member_id 或 user_id 列):只留真实会员的数据;模拟口径下只留模拟会员的数据。 */
  74. public static function excludeTestMembers($query, string $column = 'member_id', string $userColumn = 'member_id')
  75. {
  76. if (MemberAudience::isSimulated()) return MemberAudience::visibleMembers($query, $column, $userColumn);
  77. if (MemberAudience::enabled()) return $query->whereNotIn($column, self::excludedMembers($userColumn));
  78. if (!self::enabled()) return $query;
  79. return $query->whereNotIn($column, self::testMembers($userColumn));
  80. }
  81. /** 代理资金表(agent_id 列):排除测试代理。agent_id 为空的行保留。 */
  82. public static function excludeTestAgents($query, string $column = 'agent_id')
  83. {
  84. if (MemberAudience::isSimulated()) return $query->whereRaw('1 = 0');
  85. if (!self::enabled()) return $query;
  86. return $query->where(fn ($q) => $q->whereNull($column)->orWhereNotIn($column, self::testAgentIds()));
  87. }
  88. }