用户管理模块
用户管理是管理后台的第一个模块,也是贯穿产品生命周期的核心模块。一个 AI 产品出海之后,无论是 SaaS 订阅场景还是企业内部运营场景,都需要对终端用户、管理员、运营人员进行统一的身份管理、权限分配和行为管控。本文从功能设计、数据库设计、权限控制和安全考虑四个维度展开,讨论用户管理模块的工程实现方法。
一、核心功能设计
用户管理模块的前端交互可以拆解为五个基本动作:列表、搜索、筛选、详情、操作。这五个动作覆盖了管理员对用户数据「看、找、查、改」的完整链路。
1.1 用户列表
列表页是用户管理的入口,核心目标是让管理员快速建立对全量用户的「感知」。一个合格的列表页至少需要包含以下能力:
分页与虚拟滚动。 当用户量超过几千条时,分页是刚需。对于十万级以上的用户列表,前端应采用虚拟滚动(Virtual Scroll)来避免 DOM 节点过多导致的卡顿。常见方案有 react-virtuoso、@tanstack/virtual 等。
列配置与排序。 列表的列应支持拖拽排序、显隐切换和宽度记忆。管理员关注的字段因角色不同而不同——客服关注最近登录时间,运营关注订阅计划,技术支持关注账户状态。
批量操作。 批量启用、批量禁用、批量分配角色、批量导出数据。批量操作需要考虑异步执行和进度反馈,避免大数据量操作阻塞主线程。
状态标识。 用户列表中的每一行应通过标签(Tag)清晰展示关键状态:账户状态(正常 / 禁用 / 待验证)、订阅计划(Free / Pro / Enterprise)、注册来源(Email / Google OAuth / GitHub)。
1.2 搜索
搜索需要支持多维度的模糊匹配。典型的搜索场景包括:按用户名搜索、按邮箱搜索、按手机号搜索、按用户 ID 精确搜索。
实现上有两种主流方案:
数据库层搜索。 对于小规模数据(< 10 万),可以直接使用 LIKE 或 ILIKE(PostgreSQL)做模糊查询,配合 GIN 索引(pg_trgm)保证性能。
搜索引擎层搜索。 对于大规模数据或需要分词搜索的场景,引入 Elasticsearch 或 Meilisearch 是更稳妥的选择。搜索引擎支持全文检索、拼音搜索、纠错建议等高级能力。
1.3 筛选
筛选是搜索的补充,用于缩小结果集。常见的筛选维度:
| 筛选维度 | 可选值 | 说明 |
|---|---|---|
| 账户状态 | 正常 / 禁用 / 待验证 / 已注销 | 单选或全选 |
| 注册来源 | Email / Google / GitHub / Apple | 多选 |
| 订阅计划 | Free / Pro / Enterprise | 多选 |
| 注册时间 | 日期范围选择器 | 支持预设(近 7 天、近 30 天)和自定义 |
| 最近登录 | 日期范围选择器 | 用于识别沉默用户 |
| 角色 | 超级管理员 / 普通管理员 / 运营 / 开发者 | 多选 |
筛选条件应支持 URL 参数持久化,这样管理员可以分享带有筛选条件的链接给同事,也方便做浏览器书签。
1.4 用户详情
用户详情页需要从多个维度聚合数据,常见分为以下几个 Tab:
基本信息。 用户名、邮箱、手机号、头像、注册时间、最后登录时间、注册来源。敏感字段(手机号、邮箱)需要根据当前操作者的权限做脱敏展示。
账户状态。 账户是否启用、邮箱是否验证、是否启用两步验证(2FA)、账户创建和更新时间。
角色与权限。 当前用户拥有的角色列表、继承的权限列表。这里需要区分「直接分配的权限」和「通过角色继承的权限」,避免管理员混淆。
订阅与计费。 当前订阅计划、订阅起止时间、账单历史、用量统计(API 调用次数、存储空间等)。
操作日志。 该用户的关键操作记录,包括登录、密码修改、权限变更、API Key 创建等。操作日志是安全审计的基础。
1.5 用户操作
管理员对用户可执行的操作需要严格控制:
| 操作 | 风险等级 | 所需权限 | 说明 |
|---|---|---|---|
| 查看用户信息 | 低 | user:read | 基础只读操作 |
| 编辑用户信息 | 中 | user:update | 修改用户名、头像等非敏感字段 |
| 重置密码 | 高 | user:reset_password | 需要二次确认 |
| 禁用 / 启用账户 | 高 | user:toggle_status | 禁用后该用户所有会话应失效 |
| 分配角色 | 高 | user:assign_role | 角色变更应记录审计日志 |
| 删除用户 | 极高 | user:delete | 建议软删除 + 冷却期 |
| 导出数据 | 中 | user:export | 需要记录导出操作本身 |
高风险操作应要求二次确认(输入密码或 MFA 验证码),并在操作完成后发送通知给被操作的用户。
二、数据库设计
用户管理的数据库设计直接影响查询性能、扩展性和数据一致性。以下是一个适用于中大型 SaaS 产品的参考设计。
2.1 核心表结构
-- 用户主表
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email VARCHAR(255) NOT NULL,
username VARCHAR(100) NOT NULL,
password_hash VARCHAR(255), -- bcrypt/argon2 哈希,不存明文
phone VARCHAR(20),
avatar_url TEXT,
status VARCHAR(20) NOT NULL DEFAULT 'active',
-- active / disabled / pending_verification / deactivated
email_verified BOOLEAN NOT NULL DEFAULT false,
phone_verified BOOLEAN NOT NULL DEFAULT false,
mfa_enabled BOOLEAN NOT NULL DEFAULT false,
registration_source VARCHAR(30) NOT NULL DEFAULT 'email',
-- email / google / github / apple
last_login_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
deleted_at TIMESTAMPTZ, -- 软删除标记
CONSTRAINT uq_users_email UNIQUE (email),
CONSTRAINT uq_users_username UNIQUE (username)
);
-- 角色表
CREATE TABLE roles (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(50) NOT NULL, -- super_admin / admin / operator / developer
display_name VARCHAR(100) NOT NULL,
description TEXT,
is_system BOOLEAN NOT NULL DEFAULT false, -- 系统内置角色不可删除
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
CONSTRAINT uq_roles_name UNIQUE (name)
);
-- 权限表
CREATE TABLE permissions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
code VARCHAR(100) NOT NULL, -- user:read / user:update / user:delete
name VARCHAR(100) NOT NULL,
module VARCHAR(50) NOT NULL, -- 所属模块:user / content / billing
description TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
CONSTRAINT uq_permissions_code UNIQUE (code)
);
-- 角色-权限关联表
CREATE TABLE role_permissions (
role_id UUID NOT NULL REFERENCES roles(id) ON DELETE CASCADE,
permission_id UUID NOT NULL REFERENCES permissions(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (role_id, permission_id)
);
-- 用户-角色关联表
CREATE TABLE user_roles (
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
role_id UUID NOT NULL REFERENCES roles(id) ON DELETE CASCADE,
granted_by UUID NOT NULL REFERENCES users(id), -- 谁分配的
granted_at TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (user_id, role_id)
);2.2 索引策略
-- 高频查询字段索引
CREATE INDEX idx_users_email ON users (email) WHERE deleted_at IS NULL;
CREATE INDEX idx_users_status ON users (status) WHERE deleted_at IS NULL;
CREATE INDEX idx_users_created_at ON users (created_at DESC) WHERE deleted_at IS NULL;
CREATE INDEX idx_users_last_login ON users (last_login_at DESC) WHERE deleted_at IS NULL;
CREATE INDEX idx_users_registration_source ON users (registration_source) WHERE deleted_at IS NULL;
-- 模糊搜索加速(PostgreSQL pg_trgm 扩展)
CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE INDEX idx_users_username_trgm ON users USING gin (username gin_trgm_ops);
CREATE INDEX idx_users_email_trgm ON users USING gin (email gin_trgm_ops);
-- 复合索引:常见筛选组合
CREATE INDEX idx_users_status_created ON users (status, created_at DESC) WHERE deleted_at IS NULL;索引设计需要注意:所有索引都应加上 WHERE deleted_at IS NULL 条件(部分索引),避免为已软删除的数据建立索引。模糊搜索使用 pg_trgm 的 GIN 索引,比传统 B-Tree 更适合 LIKE '%keyword%' 模式。
2.3 关联关系设计
用户管理涉及的核心关联可以用下面的关系图表示:
user_profiles 用于存储扩展信息(公司名、职位、偏好设置等),与主表分离可以避免主表过宽。operation_logs 和 login_logs 是审计的基础,下文会详细讨论。
三、权限控制
权限控制是用户管理模块的核心复杂度所在。业界主流方案是 RBAC(Role-Based Access Control),但在实际工程中需要根据业务场景做适配。
3.1 RBAC 基础模型
RBAC 的核心思路是:用户不直接关联权限,而是通过「角色」间接获取权限。这样做的好处是:
- 批量管理:给 100 个运营人员分配「运营」角色,比逐个分配 10 个权限高效得多。
- 灵活调整:角色权限变更时,所有关联用户自动继承,无需逐一修改。
- 职责清晰:角色本身就是职责的抽象,便于理解和沟通。
标准 RBAC 模型包含三个层级:用户(User)→ 角色(Role)→ 权限(Permission)。权限又可以分为:
| 权限类型 | 说明 | 示例 |
|---|---|---|
| 菜单权限 | 控制左侧导航菜单的显隐 | menu:user_management |
| 页面权限 | 控制页面的访问能力 | page:user_list |
| 操作权限 | 控制按钮级操作 | button:user:delete |
| 数据权限 | 控制可见数据范围 | 只能看自己部门的用户 |
| API 权限 | 控制后端接口的调用 | POST /api/users |
3.2 操作权限
操作权限是最细粒度的控制维度。实现方式通常是:
前端控制。 根据用户角色返回的权限列表,控制按钮的显隐。但这只是 UX 层面的控制,不能替代后端校验。
后端控制。 每个 API 接口声明所需的权限码,在中间件层校验。典型实现:
// 权限校验中间件
function requirePermission(...requiredPermissions: string[]) {
return async (c: Context, next: Next) => {
const user = c.get('currentUser')
const userPermissions = await getUserPermissions(user.id)
const hasAll = requiredPermissions.every(p => userPermissions.includes(p))
if (!hasAll) {
return c.json({ error: '权限不足' }, 403)
}
await next()
}
}
// 路由中使用
app.delete('/api/users/:id',
requirePermission('user:delete'),
async (c) => {
const userId = c.req.param('id')
await userService.softDelete(userId)
return c.json({ success: true })
}
)3.3 数据权限
数据权限控制的是「能看到哪些数据」,而操作权限控制的是「能做什么操作」。数据权限的常见模型:
全局可见。 超级管理员可以查看所有用户数据。
部门/组织可见。 部门管理员只能查看本部门下的用户。实现方式是在用户表中添加 organization_id 字段,查询时加上 WHERE organization_id = :currentOrgId。
个人可见。 普通用户只能查看自己的数据。这是最基本的隔离。
数据权限的实现建议放在 SQL 查询层统一处理,通过中间件自动注入过滤条件,而不是在每个业务方法中手动拼接:
// 数据权限中间件示例
function applyDataScope(query: SelectQuery, currentUser: User) {
if (currentUser.isSuperAdmin) return query // 超管无限制
if (currentUser.scope === 'organization') {
return query.where('organization_id', currentUser.organizationId)
}
// 默认只看自己
return query.where('id', currentUser.id)
}3.4 权限对比
| 维度 | RBAC | ABAC | ACL |
|---|---|---|---|
| 全称 | Role-Based Access Control | Attribute-Based Access Control | Access Control List |
| 核心概念 | 用户→角色→权限 | 基于属性的策略规则 | 资源→权限列表 |
| 适用场景 | 企业后台、SaaS 管理后台 | 复杂条件判断(时间、地点、设备) | 文件系统、资源级别控制 |
| 灵活度 | 中等 | 高 | 低 |
| 实现复杂度 | 低 | 高 | 中 |
| 性能开销 | 低(角色数量有限) | 中(需实时计算属性) | 高(大列表遍历) |
对于大多数 AI 产品出海场景,RBAC 已经足够。当需要处理「只允许在工作时间从公司 IP 登录的管理员执行敏感操作」这类复合条件时,可以在 RBAC 基础上叠加 ABAC 策略。
四、安全考虑
用户管理模块涉及大量敏感数据,安全设计需要贯穿数据生命周期的每个环节。
4.1 敏感信息保护
密码存储。 绝对不要存储明文密码。推荐使用 Argon2id(首选)或 bcrypt(cost factor ≥ 12)。加盐是必须的,Argon2 和 bcrypt 都内置了盐值机制。
import { hash, verify } from '@node-rs/argon2'
// 密码哈希
const passwordHash = await hash(password, {
algorithm: 'argon2id',
memoryCost: 65536, // 64MB
timeCost: 3,
parallelism: 4,
})
// 密码验证
const isValid = await verify(passwordHash, password)数据脱敏。 列表中展示手机号时部分隐藏(138****1234),邮箱部分隐藏(u***[email protected])。详情页根据操作者权限决定是否展示完整信息。
传输安全。 所有涉及用户数据的接口必须使用 HTTPS。敏感字段(密码、手机号)在前端提交时应额外做前端加密(如 RSA),防止 HTTPS 卸载后的中间人攻击。
日志脱敏。 操作日志中不要记录密码、Token 等敏感信息。手机号、邮箱等字段在日志中应脱敏处理。
4.2 操作审计
审计日志是安全合规的基础要求,也是 SOC 2、GDPR 等认证的必要条件。
-- 操作审计日志表
CREATE TABLE operation_logs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id),
action VARCHAR(50) NOT NULL, -- user:disable / user:reset_password / user:assign_role
target_type VARCHAR(50) NOT NULL, -- user / role / permission
target_id UUID, -- 被操作对象的 ID
detail JSONB, -- 操作详情(变更前后对比)
ip_address INET,
user_agent TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_operation_logs_user ON operation_logs (user_id, created_at DESC);
CREATE INDEX idx_operation_logs_action ON operation_logs (action, created_at DESC);审计日志中应记录「变更前后的值」,例如将用户从 active 改为 disabled,日志中应同时包含 before: { status: 'active' } 和 after: { status: 'disabled' }。
4.3 安全措施对比
| 安全措施 | 保护目标 | 实现方式 | 优先级 |
|---|---|---|---|
| 密码哈希 | 防止密码泄露 | Argon2id / bcrypt | P0 - 必须 |
| HTTPS 传输 | 防止中间人攻击 | TLS 1.2+ | P0 - 必须 |
| 数据脱敏 | 防止敏感信息暴露 | 字段级脱敏规则 | P0 - 必须 |
| 操作审计 | 满足合规要求 | 审计日志表 + 异步写入 | P0 - 必须 |
| MFA 两步验证 | 防止账号被盗 | TOTP / WebAuthn | P1 - 推荐 |
| 登录限流 | 防止暴力破解 | 滑动窗口限流 + 验证码 | P1 - 推荐 |
| IP 白名单 | 限制管理后台访问 | Nginx/Cloudflare 规则 | P2 - 高安全场景 |
| 会话管理 | 防止会话劫持 | JWT 短有效期 + Refresh Token 轮换 | P1 - 推荐 |
4.4 登录安全
登录安全是用户管理的重要延伸。推荐的安全策略:
登录限流。 同一个 IP 在 5 分钟内最多尝试 10 次登录,超出后要求输入验证码或暂时锁定。同一个账号连续失败 5 次后锁定 15 分钟。
异常登录检测。 记录每次登录的 IP 地理位置和设备指纹,当检测到异常(异地登录、新设备登录)时触发额外验证。
会话管理。 管理员后台的会话有效期应短于普通用户(建议 2 小时),超时后需要重新认证。支持「强制下线」功能,管理员可以终止任意用户的当前会话。
五、实现方法
下面以一个典型的 Next.js + Hono API 项目为例,展示用户管理模块的关键实现。
5.1 用户列表 API
// apps/api/src/modules/user/service.ts
import { db } from '@/db'
import { users, userRoles, roles } from '@/db/schema'
import { and, eq, ilike, inArray, desc, isNull, sql } from 'drizzle-orm'
interface ListUsersParams {
page: number
pageSize: number
keyword?: string
status?: string[]
source?: string[]
roleIds?: string[]
createdAfter?: string
createdBefore?: string
sortBy?: string
sortOrder?: 'asc' | 'desc'
}
export async function listUsers(params: ListUsersParams) {
const { page, pageSize, keyword, status, source, roleIds, createdAfter, createdBefore, sortBy = 'created_at', sortOrder = 'desc' } = params
const conditions = [isNull(users.deletedAt)]
// 关键词搜索
if (keyword) {
conditions.push(
sql`(${ilike(users.username, `%${keyword}%`)} OR ${ilike(users.email, `%${keyword}%`)})`
)
}
// 状态筛选
if (status?.length) {
conditions.push(inArray(users.status, status))
}
// 注册来源筛选
if (source?.length) {
conditions.push(inArray(users.registrationSource, source))
}
// 时间范围筛选
if (createdAfter) {
conditions.push(sql`${users.createdAt} >= ${createdAfter}::timestamptz`)
}
if (createdBefore) {
conditions.push(sql`${users.createdAt} <= ${createdBefore}::timestamptz`)
}
const whereClause = and(...conditions)
// 查询总数和数据
const [totalResult, data] = await Promise.all([
db.select({ count: sql<number>`count(*)` })
.from(users)
.where(whereClause),
db.select({
id: users.id,
email: users.email,
username: users.username,
status: users.status,
avatarUrl: users.avatarUrl,
registrationSource: users.registrationSource,
emailVerified: users.emailVerified,
mfaEnabled: users.mfaEnabled,
lastLoginAt: users.lastLoginAt,
createdAt: users.createdAt,
})
.from(users)
.where(whereClause)
.orderBy(sortOrder === 'desc' ? desc(users[sortBy]) : users[sortBy])
.limit(pageSize)
.offset((page - 1) * pageSize),
])
return {
data,
total: totalResult[0].count,
page,
pageSize,
}
}5.2 路由层
// apps/api/src/routes/admin/users/index.ts
import { Hono } from 'hono'
import { listUsers } from '@/modules/user/service'
import { requirePermission } from '@/middleware/permission'
const userRoutes = new Hono()
userRoutes.get('/',
requirePermission('user:read'),
async (c) => {
const query = c.req.query()
const result = await listUsers({
page: Number(query.page) || 1,
pageSize: Math.min(Number(query.pageSize) || 20, 100),
keyword: query.keyword,
status: query.status?.split(','),
source: query.source?.split(','),
roleIds: query.roleIds?.split(','),
createdAfter: query.createdAfter,
createdBefore: query.createdBefore,
sortBy: query.sortBy,
sortOrder: query.sortOrder as 'asc' | 'desc',
})
return c.json(result)
}
)
export default userRoutes5.3 前端列表页核心逻辑
// apps/web/src/app/[locale]/(admin)/admin/users/_components/user-table.tsx
'use client'
import { useQuery } from '@tanstack/react-query'
import { useSearchParams, useRouter } from 'next/navigation'
function useUserList() {
const searchParams = useSearchParams()
const router = useRouter()
const params = {
page: Number(searchParams.get('page')) || 1,
pageSize: Number(searchParams.get('pageSize')) || 20,
keyword: searchParams.get('keyword') || undefined,
status: searchParams.get('status')?.split(',') || undefined,
source: searchParams.get('source')?.split(',') || undefined,
createdAfter: searchParams.get('createdAfter') || undefined,
createdBefore: searchParams.get('createdBefore') || undefined,
}
return useQuery({
queryKey: ['admin-users', params],
queryFn: () => fetchUsers(params),
placeholderData: (prev) => prev, // 避免切换页码时闪烁
})
}5.4 用户操作:禁用账户
// apps/api/src/modules/user/service.ts
export async function disableUser(operatorId: string, targetUserId: string) {
// 不允许禁用自己
if (operatorId === targetUserId) {
throw new Error('不能禁用自己的账户')
}
// 不允许禁用超级管理员(如果操作者不是超管)
const targetRoles = await getUserRoles(targetUserId)
const operatorRoles = await getUserRoles(operatorId)
if (targetRoles.some(r => r.name === 'super_admin') &&
!operatorRoles.some(r => r.name === 'super_admin')) {
throw new Error('权限不足,无法禁用超级管理员')
}
// 事务:更新状态 + 记录审计日志 + 销毁会话
await db.transaction(async (tx) => {
await tx.update(users)
.set({ status: 'disabled', updatedAt: new Date() })
.where(eq(users.id, targetUserId))
await tx.insert(operationLogs).values({
userId: operatorId,
action: 'user:disable',
targetType: 'user',
targetId: targetUserId,
detail: { before: { status: 'active' }, after: { status: 'disabled' } },
})
// 销毁该用户所有活跃会话
await tx.delete(sessions)
.where(eq(sessions.userId, targetUserId))
})
}六、模块设计流程
七、实际案例
案例一:AI SaaS 平台的用户管理
某 AI SaaS 产品(类似 Jasper、Copy.ai)的用户管理模块设计:
业务背景。 产品支持 Email 和 Google OAuth 两种注册方式,用户分为 Free / Pro / Enterprise 三个订阅层级,管理员需要管理终端用户和内部运营人员两类角色。
设计决策。
终端用户和内部管理员使用同一张 users 表,通过 user_type 字段(end_user / staff)区分。这样做的好处是统一身份管理,避免两套用户体系的维护成本。
权限模型采用简化版 RBAC:终端用户不纳入 RBAC 体系(其权限由订阅计划决定),内部管理员使用标准 RBAC。
数据权限方面,客服角色只能查看用户基本信息和订阅信息,不能查看操作日志中的敏感操作详情。技术支持角色可以查看完整信息但不能执行禁用操作。
效果。 该系统上线后管理 5 万+ 终端用户和 20+ 内部管理员,列表页加载时间稳定在 200ms 以内(配合 Redis 缓存用户统计数据),操作审计日志通过异步队列写入,不影响主流程性能。
案例二:AI 开发者平台的组织级用户管理
某 AI 开发者平台(类似 Replicate、Hugging Face)的用户管理涉及组织维度:
业务背景。 平台支持多租户模式,每个组织有自己的成员列表和角色体系。用户可能同时属于多个组织。
设计决策。
在 users 表之外引入 organizations 表和 organization_members 关联表。用户在组织内的角色是独立于全局角色的——一个用户可以在组织 A 是管理员,在组织 B 是普通成员。
CREATE TABLE organizations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(100) NOT NULL,
plan VARCHAR(20) NOT NULL DEFAULT 'free',
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE organization_members (
organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
org_role VARCHAR(20) NOT NULL DEFAULT 'member', -- owner / admin / member / viewer
joined_at TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (organization_id, user_id)
);数据隔离。 组织 A 的管理员不能查看组织 B 的成员信息。这通过在查询层强制注入 organization_id 过滤条件实现,与前面提到的数据权限中间件配合使用。
API Key 管理。 每个组织有独立的 API Key 池,Key 归属于组织而非个人。管理员可以创建、撤销组织级别的 API Key,查看每个 Key 的用量。
八、检查清单
在用户管理模块上线前,逐项确认以下要点:
- 密码存储使用 Argon2id 或 bcrypt,不存储明文
- 所有敏感数据传输走 HTTPS
- 列表展示手机号、邮箱时做了脱敏处理
- 操作权限已实现后端校验,不仅依赖前端按钮显隐
- 数据权限隔离正确,不同角色看不到超出范围的数据
- 高风险操作(禁用账户、重置密码、删除用户)需要二次确认
- 操作审计日志完整记录了「谁在什么时间做了什么」
- 用户删除采用软删除,保留数据可恢复能力
- 登录接口有限流策略,防止暴力破解
- 管理员会话有效期短于普通用户,支持强制下线
- 批量操作有异步处理和进度反馈,不会阻塞主线程
- 列表搜索在大数据量下有索引或搜索引擎支持
- 筛选条件可持久化到 URL,支持分享和书签
- 禁用账户后自动销毁该用户所有活跃会话
- 审计日志中的敏感字段已脱敏
九、参考资料
- 用户管理模块设计 - 人人都是产品经理 - 用户管理模块的产品设计思路与功能拆解。
- SaaS User Management: A Comprehensive Guide - Zluri - SaaS 场景下用户管理的全面指南,涵盖认证、授权和合规。
- Top User Management Features for SaaS - WorkOS - 用户管理核心功能清单与实现建议。
- Top 8 User Management Features Within Your App - Frontegg - 应用内用户管理功能设计,包括角色管理和邀请机制。
- How to Design a Database for Identity Management Systems - GeeksforGeeks - 身份管理系统的数据库设计方法论。
- Database Structure for User and Role Management - LinkedIn - 用户与角色管理的数据库结构设计实践。
- 用户管理系统 - 用户权限设计从入门到精通 - 知乎 - RBAC 权限模型的系统讲解与实现指导。
- A Database Design for User Profiles - Redgate - 用户资料数据库模型设计参考。