diff --git a/backend/app/Service/ScopeBitmapService.php b/backend/app/Service/ScopeBitmapService.php new file mode 100644 index 0000000..6dc9f06 --- /dev/null +++ b/backend/app/Service/ScopeBitmapService.php @@ -0,0 +1,176 @@ +> $bit) & 1) { + $store_ids[] = $i * 8 + $bit + 1; + } + } + } + + return $store_ids; + } + + /** + * 检查 bitmap 中是否包含指定 store_id + * + * @param string $bitmap + * @param int $store_id + * @return bool + */ + public function has(string $bitmap, int $store_id): bool + { + $bit_pos = $store_id - 1; + $byte_index = intdiv($bit_pos, 8); + + if ($byte_index >= strlen($bitmap)) { + return false; + } + + $bit_offset = $bit_pos % 8; + return (bool) ((ord($bitmap[$byte_index]) >> $bit_offset) & 1); + } + + /** + * 合并多个 bitmap(OR 运算) + * + * @param string ...$bitmaps + * @return string + */ + public function merge(string ...$bitmaps): string + { + $bitmaps = array_filter($bitmaps, fn(string $b): bool => $b !== ''); + + if (empty($bitmaps)) { + return ''; + } + + $max_len = max(array_map('strlen', $bitmaps)); + $result = str_repeat("\0", $max_len); + + foreach ($bitmaps as $bitmap) { + for ($i = 0; $i < strlen($bitmap); $i++) { + $result[$i] = chr(ord($result[$i]) | ord($bitmap[$i])); + } + } + + return $result; + } + + /** + * 根据用户的 user_data_scopes 记录构建 scope bitmap + * 处理 company/platform/store 三种 scope_type 的解析和合并 + * + * @param int $user_id + * @return string + */ + public function buildForUser(int $user_id): string + { + $scopes = UserDataScope::query()->where('user_id', $user_id)->get(); + + if ($scopes->isEmpty()) { + return ''; + } + + $store_ids = []; + $grouped = $scopes->groupBy('scope_type'); + + // company 类型:查该 company 下所有 store + if ($company_scopes = $grouped->get('company')) { + $company_ids = $company_scopes->pluck('scope_id')->toArray(); + $store_ids = array_merge( + $store_ids, + Store::query()->whereIn('company_id', $company_ids)->pluck('id')->toArray() + ); + } + + // platform 类型:查该 platform 下所有 store + if ($platform_scopes = $grouped->get('platform')) { + $platform_ids = $platform_scopes->pluck('scope_id')->toArray(); + $store_ids = array_merge( + $store_ids, + Store::query()->whereIn('platform_id', $platform_ids)->pluck('id')->toArray() + ); + } + + // store 类型:直接使用 + if ($store_scopes = $grouped->get('store')) { + $store_ids = array_merge($store_ids, $store_scopes->pluck('scope_id')->toArray()); + } + + $store_ids = array_unique(array_map('intval', $store_ids)); + return $this->encode($store_ids); + } + + /** + * 根据 developer 维护的 platform 构建 scope bitmap + * + * @param int $user_id + * @return string + */ + public function buildForDeveloper(int $user_id): string + { + $platform_ids = Platform::query()->where('developer_id', $user_id)->pluck('id')->toArray(); + + if (empty($platform_ids)) { + return ''; + } + + $store_ids = Store::query()->whereIn('platform_id', $platform_ids)->pluck('id')->toArray(); + $store_ids = array_map('intval', $store_ids); + return $this->encode($store_ids); + } +} diff --git a/backend/app/Service/ScopeTableManager.php b/backend/app/Service/ScopeTableManager.php new file mode 100644 index 0000000..61bdb74 --- /dev/null +++ b/backend/app/Service/ScopeTableManager.php @@ -0,0 +1,99 @@ +initTable(); + } + + /** + * 初始化 Swoole\Table + */ + protected function initTable(): void + { + $this->table = new Table(1024); + $this->table->column('role', Table::TYPE_STRING, 32); + $this->table->column('scope', Table::TYPE_STRING, 4096); + $this->table->column('version', Table::TYPE_INT, 8); + $this->table->create(); + } + + public function getTable(): Table + { + return $this->table; + } + + /** + * 获取用户 scope 数据,未命中时懒加载 + * + * @param int $user_id + * @return array{role: string, scope: string, version: int}|null + */ + public function getUserScope(int $user_id): ?array + { + $key = (string) $user_id; + $row = $this->table->get($key); + + if ($row !== false) { + return $row; + } + + // Table 未命中,从数据库加载 + return $this->rebuildUserScope($user_id); + } + + /** + * 重建用户 scope 并写入 Table + * + * @param int $user_id + * @return array{role: string, scope: string, version: int}|null + */ + public function rebuildUserScope(int $user_id): ?array + { + $user = User::query()->with('role')->find($user_id); + if (!$user || !$user->role) { + return null; + } + + $role_name = $user->role->name; + + // administrator 不需要 bitmap + if ($role_name === 'administrator') { + $bitmap = ''; + } elseif ($role_name === 'developer') { + $bitmap = $this->bitmapService->buildForDeveloper($user_id); + } else { + $bitmap = $this->bitmapService->buildForUser($user_id); + } + + $data = [ + 'role' => $role_name, + 'scope' => $bitmap, + 'version' => time(), + ]; + + $this->table->set((string) $user_id, $data); + return $data; + } + + /** + * 清除用户 scope(权限变更时先清再重建) + * + * @param int $user_id + * @return void + */ + public function invalidateUser(int $user_id): void + { + $this->table->del((string) $user_id); + } +}