hasColumn('users', 'user_type'); } /** 模拟会员的某一列(member_id / user_id / id)子查询。 */ public static function simulatedMembers(string $column = 'member_id') { return DB::table('users')->select($column)->where('user_type', self::TYPE_SIMULATED)->whereNotNull($column); } /** 查询里已有 users 表(或别名)时:REAL 排除模拟会员,SIMULATED 只留模拟会员。 */ public static function visibleUsers($query, string $alias = 'users') { if (!self::enabled()) return self::isSimulated() ? $query->whereRaw('1 = 0') : $query; return $query->where($alias . '.user_type', self::isSimulated() ? '=' : '<>', self::TYPE_SIMULATED); } /** 查询里没有 users 表时,按会员列过滤。$userColumn 是 $column 对应的 users 列。$column 为空的行在 REAL 口径下保留。 */ public static function visibleMembers($query, string $column = 'member_id', string $userColumn = 'member_id') { if (!self::enabled()) return self::isSimulated() ? $query->whereRaw('1 = 0') : $query; if (self::isSimulated()) return $query->whereIn($column, self::simulatedMembers($userColumn)); return $query->where(fn ($q) => $q->whereNull($column)->orWhereNotIn($column, self::simulatedMembers($userColumn))); } /** 原生 SQL 用:users 别名为 $alias 时追加的条件(以 AND 开头)。 */ public static function rawVisibleUsers(string $alias = 'u'): string { if (!self::enabled()) return self::isSimulated() ? ' AND 1 = 0' : ''; return " AND {$alias}.user_type " . (self::isSimulated() ? '=' : '<>') . ' ' . self::TYPE_SIMULATED; } /** 某个 member_id 是否是模拟会员(同一连接内缓存;用户类型创建后不会变)。 */ public static function isSimulatedMember($memberId): bool { if ($memberId === null || $memberId === '' || !self::enabled()) return false; self::$memo ??= new \WeakMap(); $connection = DB::connection(); $known = self::$memo[$connection] ?? []; $key = (string) $memberId; if (!isset($known[$key])) { $known[$key] = DB::table('users')->where('member_id', $key)->where('user_type', self::TYPE_SIMULATED)->exists(); self::$memo[$connection] = $known; } return $known[$key]; } /** 新建模拟会员后登记,免得同一请求里缓存了“不是模拟会员”。 */ public static function remember($memberId): void { self::$memo ??= new \WeakMap(); $connection = DB::connection(); $known = self::$memo[$connection] ?? []; $known[(string) $memberId] = true; self::$memo[$connection] = $known; } }