| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 |
- <?php
- namespace App\Support;
- use Illuminate\Support\Facades\DB;
- /**
- * 会员受众:区分正式数据和模拟数据(users.user_type = 2 的后台模拟会员)。
- * 默认 REAL:所有列表和统计都看不到模拟会员;SIMULATED:只看模拟会员,仅 /admin/simulation/* 通过 simulated() 进入。
- * 游客(users.from = 2 且不是模拟会员)是否参与由调用方决定,这里只管模拟会员。
- */
- final class MemberAudience
- {
- public const REAL = 'real';
- public const SIMULATED = 'simulated';
- public const TYPE_SIMULATED = 2;
- private static string $mode = self::REAL;
- private static ?\WeakMap $enabled = null;
- private static ?\WeakMap $memo = null;
- public static function isSimulated(): bool
- {
- return self::$mode === self::SIMULATED;
- }
- /** 在“仅模拟会员”口径下执行 $fn,结束后一定还原。 */
- public static function simulated(callable $fn)
- {
- $previous = self::$mode;
- self::$mode = self::SIMULATED;
- try {
- return $fn();
- } finally {
- self::$mode = $previous;
- }
- }
- /** users.user_type 存在才区分(迁移执行前不过滤)。按数据库连接缓存。 */
- public static function enabled(): bool
- {
- self::$enabled ??= new \WeakMap();
- $connection = DB::connection();
- return self::$enabled[$connection] ??= DB::getSchemaBuilder()->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;
- }
- }
|