权限与角色
本页是 DSH-Guild 权限模型的完整规格:每个权限位能做什么、角色怎么叠加、频道覆盖按什么顺序生效、哪些动作只有 owner 能做。想「照着配一遍」看使用指南,想查「这个功能要哪个权限」直接看下面的动作 × 权限矩阵。
一、概念模型
DSH-Guild 采用 Discord 式 权限模型,三层结构:
| 层级 | 作用范围 | 说明 |
|---|---|---|
| 社区级权限 | 整个社区 | 挂在角色上的基础权限位,如「管理社区」「封禁成员」 |
| 频道级权限 | 单个频道 | 既可挂在角色上,也可作为频道覆盖(overwrite)的目标 |
| 身份 | 用户 ↔ 社区 | owner / 成员 / 非成员三种状态,决定权限判定的起点 |
三种身份
| 身份 | 判定依据 | 权限 |
|---|---|---|
| owner | communities.ownerId 等于该用户 | 恒定拥有全部权限位,忽略一切频道覆盖;删社区 / 转让所有权也仅 owner 可为 |
| 成员 | community_members 表有行 | 基础权限 = 所持角色权限的并集,再叠加频道覆盖 |
| 非成员 | 无成员行 | 权限恒为 0,看不到任何频道(公开社区也不例外) |
owner 不是「一个角色」
owner 是社区表上的一个字段,不占角色槽位、不可被剥夺、也不会出现在角色列表里。预置的那个「管理员」角色是普通角色,和 owner 完全是两回事 —— 详见第四节。
二、权限位总表
共 11 个权限位。作用域决定了它能否作为频道覆盖的目标:channel 可以,community 只能挂在角色上。
| # | 权限位 | 中文名 | 作用域 | 能做什么 |
|---|---|---|---|---|
| 0 | VIEW_CHANNEL | 查看频道 | channel | 看不到则频道不下发、不可订阅、不可读消息 |
| 1 | SEND_MESSAGES | 发送消息 | channel | 在频道与讨论组里发言 |
| 2 | CREATE_THREAD | 创建讨论组 | channel | 在频道里开讨论组 / 话题 |
| 3 | MANAGE_THREADS | 管理讨论组 | channel | 改名 / 归档 / 删除讨论组、加人、标记解决 |
| 4 | MANAGE_MESSAGES | 管理消息 | channel | 改 / 删 / 撤回他人消息,置顶 / 取消置顶 |
| 5 | MANAGE_CHANNEL | 管理频道 | channel | 新建 / 改名 / 删除频道、调整顺序、管理频道权限覆盖 |
| 6 | MANAGE_COMMUNITY | 管理社区 | community | 改社区资料、隐私与短标识(删除 / 转让仅 owner) |
| 7 | MANAGE_ROLES | 管理角色 | community | 增删改角色、调整层级、给成员分配角色(受层级限制) |
| 8 | INVITE_MEMBERS | 邀请成员 | community | 邀请已注册用户加入社区 |
| 9 | KICK_MEMBERS | 移除成员 | community | 把成员移出社区(可被邀请码重新加入) |
| 10 | BAN_MEMBERS | 封禁成员 | community | 移出并禁止重新加入、管理封禁名单 |
两个预设权限组合
| 名称 | 内容 | 用在哪 |
|---|---|---|
ALL_PERMISSIONS | 全部 11 个位 | owner 恒定拥有;建社区时预置的「管理员」角色也按它初始化 |
DEFAULT_EVERYONE_PERMISSIONS | 查看频道 + 发送消息 + 创建讨论组 + 邀请成员 | 新建社区时 @everyone 的初始权限 |
位标志的实际取值
权限是 bitfield,位序为上表从左到右(VIEW_CHANNEL = 1 << 0,BAN_MEMBERS = 1 << 10)。ALL_PERMISSIONS = 2047。新增权限位一律追加在末尾,不复用旧位。
三、权限解析算法
计算「某人在某频道有哪些权限」严格按以下 6 步,顺序不可调换:
1) 是 owner? → 直接返回 ALL_PERMISSIONS(忽略后续所有覆盖)
2) 是成员? → 否则返回 0(社区频道一律不可见)
3) 基础权限 base = 全部持有角色的 permissions 按位或(含 @everyone)
4) @everyone 覆盖 base = (base & ~deny) | allow
5) 持有角色的覆盖 按角色 position 从低到高,逐个 base = (base & ~deny) | allow
6) 成员级覆盖 base = (base & ~deny) | allow几个关键点:
- 第 5 步的顺序是有意义的:高位角色的覆盖后应用,所以高位的
deny能压过低位的allow,反之亦然。同 position 时按targetId字典序稳定排序。 - 覆盖是「整体覆盖」而非「取并集」:
allow与deny同时存在时,该层计算完就是(基础 & ~deny) | allow,deny先清掉、allow再补上。若同一个位同时在 allow 和 deny 里,allow 胜。 - 只有
channel作用域的位能作覆盖目标。尝试用MANAGE_ROLES之类的社区级位设覆盖会被直接拒绝。 VIEW_CHANNEL是总闸:社区频道列表只下发VIEW_CHANNEL通过的频道;权限变更会实时下发,在线连接被重新校验,失去频道可见性的连接会被主动断开。
频道可见性的边界
| 场景 | 结果 |
|---|---|
| 非成员访问公开社区频道 | 不可见(权限 0) |
成员被 deny VIEW_CHANNEL | 该频道不下发、WS 连接断开 |
| 社区被设为私有 | 不影响已加入成员的可见性,只影响「能否被发现 / 加入」 |
四、角色
@everyone
每个社区必有且仅有一个 @everyone 角色,约束如下:
| 属性 | 值 |
|---|---|
isEveryone | true(每社区唯一) |
position | 恒为 0 |
| 作用方式 | 隐式作用于全体成员,member_roles 表不写行 |
| 可改名 / 删除 / 调层级 | 全部不可 |
| 权限 | 可增删权限位(初始为 DEFAULT_EVERYONE_PERMISSIONS) |
预置「管理员」角色
建社区时自动创建一个名字为 管理员 的角色,语义上是普通角色:
- 权限按创建当时的
ALL_PERMISSIONS初始化 —— 后续新增的权限位不会自动补进来; position初始为1;- 可改名、可删除、可任意增删权限位;
- 不会自动分配给任何人,需要 owner 手动分配;
- 频道覆盖对它一样生效,不会绕过。
层级与 position
- 自定义角色占
1..N的唯一槽位(@everyone恒为 0,不占槽位); - position 越大越靠上;
- 调整层级用整体重排语义:提交全部自定义角色、按从高到低排列,服务端据此重算槽位。
防提权三规则
这是权限体系里最容易被误解的部分:
| # | 规则 | 违反时 |
|---|---|---|
| 1 | 只能操作层级严格低于自己的角色与成员 | 403「不能操作层级不低于自己的角色」/「不能管理层级不低于自己的成员」 |
| 2 | 不能授予自己没有的权限位 | 403「不能授予自己没有的权限」 |
| 3 | 频道覆盖只接受频道级权限位 | 400「频道覆盖只支持频道级权限位(不含社区级权限)」 |
关于规则 1 的细节:
- 比较基准是「我持有的最高角色 position」(
@everyone不计入;没有自定义角色则记为 0); - 「严格低于」意味着层级相同也不能操作;
- 重排层级时,非 owner 只能重排层级严格低于自己的那一段,更高的角色锁在原位,且提交顺序里这段前缀的顺序必须保持不变;
- 同样的层级判定复用于踢人 / 封禁 / 改成员角色:不能管理「当前最高角色层级不低于自己」的成员。
owner 不受这三条限制
owner 跳过所有层级与授予校验,但删社区 / 转让所有权这两件敏感操作反过来只允许 owner 执行。
五、频道权限覆盖
覆盖(overwrite)是频道维度的 allow / deny 叠加,作用对象有三类:
targetType | targetId | 说明 |
|---|---|---|
everyone | @everyone(固定常量) | 对全体的频道级调整 |
role | 角色 id | 对该角色持有者生效 |
member | 用户 id | 对单个成员生效,优先级最高 |
优先级顺序
@everyone → 角色(按 position 从低到高)→ 成员。解析细节见第三节。
公告频道的自动覆盖
把频道类型切成 公告 时,系统会自动为 @everyone 写入一条只读覆盖;切回其他类型时自动撤销。这就是「公告频道默认全员只读,仅 owner 或被单独授权的角色 / 成员可发」的实现方式 —— 你也可以在此之上继续手动调整。
自锁保护
读取频道的覆盖列表走的是社区级 MANAGE_CHANNEL 校验,而不是频道级,避免管理员被自己设的频道级 deny 锁在外面无法改回来。
动作 × 权限矩阵
每个操作需要的权限,含「仅 owner」的特殊情况。
社区
| 操作 | 要求 |
|---|---|
| 创建社区 | 任意登录用户(受限额约束) |
| 查看社区详情 | 成员 |
| 改社区资料 / 隐私 / 短标识 | MANAGE_COMMUNITY |
| 转让所有权 | 仅 owner |
| 删除社区 | 仅 owner |
| 退出社区 | 成员(owner 需先转让) |
频道
| 操作 | 要求 |
|---|---|
| 新建频道 | MANAGE_CHANNEL(社区级校验) |
| 改频道资料 / 类型 / 顺序 | MANAGE_CHANNEL(频道级校验) |
| 删除频道 | MANAGE_CHANNEL(频道级校验) |
| 查看频道列表 | VIEW_CHANNEL |
| 查看 / 修改频道覆盖 | MANAGE_CHANNEL(社区级校验,防自锁) |
角色与成员
| 操作 | 要求 |
|---|---|
| 查看角色列表 | 成员 |
| 新建 / 改 / 删角色 | MANAGE_ROLES + 层级严格高于目标角色 |
| 重排角色层级 | MANAGE_ROLES + 只能动层级严格低于自己的段 |
| 给成员分配角色 | MANAGE_ROLES + 不能管理层级不低于自己的成员 + 不能授予层级不低于自己的角色 |
| 邀请用户加入 | INVITE_MEMBERS |
| 移除成员(踢人) | KICK_MEMBERS + 目标不是 owner / 自己 / 层级不低于自己者 |
| 封禁 / 解封 / 查封禁列表 | BAN_MEMBERS + 目标不是 owner / 自己 / 层级不低于自己者 |
| 查看成员 / 在线列表 | 成员 |
消息
| 操作 | 要求 |
|---|---|
| 读消息 / 置顶列表 / 已读状态 / 在线人数 | VIEW_CHANNEL |
| 发消息 | VIEW_CHANNEL + SEND_MESSAGES |
| 搜索消息 | 成员(结果按可见频道过滤) |
| 改自己的消息 | 作者本人 |
| 改他人消息 | MANAGE_MESSAGES |
| 删 / 撤回自己的消息 | 发送后 2 分钟内;超时只能编辑 |
| 删他人消息 | MANAGE_MESSAGES(不受 2 分钟限制) |
| 置顶 / 取消置顶 | MANAGE_MESSAGES(社区级校验) |
| 加 / 取消表情回应 | VIEW_CHANNEL |
讨论组(话题)
| 操作 | 要求 |
|---|---|
| 查看讨论组列表 | 频道 VIEW_CHANNEL |
| 创建讨论组 | 频道 CREATE_THREAD |
| 改讨论组(改名 / 归档 / 删除 / 加人 / 移出 / 标记解决) | MANAGE_THREADS(社区级校验) |
| 进入私密讨论组 | 成员 + 密码正确,或已在成员名单内 |
| 主动加入公开讨论组 | 成员 |
| 退出讨论组 | 成员本人 |
| 读 / 写讨论组已读状态 | 成员 |
分享
| 操作 | 要求 |
|---|---|
| 上传分享包到 R2 | 成员 |
| 登记分享 | 成员(校验社区成员身份) |
| 查看分享详情 | 成员 |
| 下载分享包 | 成员 |