---
url: /docs/auth/organizations.md
---
# 组织与角色

组织用于把多个账号放进同一成员和账单范围。个人工作区和团队工作区可以切换；工具历史和积分当前仍属于个人账号，不会随着团队切换共享给其他成员。

## 当前可以做什么

* 在工作台侧栏的工作区选择器切换个人工作区或自己所属的团队。
* 在 `/members` 创建团队；已有团队时仍可创建第二个团队。创建后自动切换到新团队。
* 按姓名或邮箱搜索整个团队的成员，每页 20 人，提供上一页、下一页和结果数量。
* 切换后重新读取会话、刷新工作区列表和成员查询，并使旧账单缓存失效。切换完成前禁用重复操作。

**邀请邮件投递、邀请接受、成员角色编辑与成员移除界面仍待开发。** 不应把现有成员目录当作完整的邀请或权限管理产品。

## 配置与代码入口

| 位置 | 内容 |
| --- | --- |
| `apps/api/lib/auth.ts` | 组织插件、会话初始组织、支付权限 |
| `db/schema/organization.ts` | 组织和成员数据 |
| `apps/app/components/layout/workspace-switcher.tsx` | 工作区切换、加载与失败恢复 |
| `apps/app/lib/queries/organization.ts` | 组织列表、创建、切换、搜索缓存 |
| `apps/app/routes/(app)/members.tsx` | 成员搜索、分页和新团队表单 |
| `apps/api/routers/organization.ts` | 带成员权限校验的跨页姓名 / 邮箱搜索 |
| `apps/api/routers/billing.ts` | 活动组织成员校验和账单查询 |

插件允许用户创建组织，每个用户创建上限为五个，创建者角色为 `owner`。限制由服务端执行；组织选择器展示的是自己所属的组织，不能据其总数判断自己创建的组织数量。

## 角色与权限

| 角色值   | 中文含义 | 当前组织账单权限               |
| -------- | -------- | ------------------------------ |
| `owner`  | 所有者   | 可以管理                       |
| `admin`  | 管理员   | 可以管理                       |
| `member` | 普通成员 | 可以查看所属组织套餐，不能管理 |

创建与切换调用 Better Auth 组织插件，由插件校验成员关系。成员搜索新增 tRPC 读取接口，因为插件原有过滤字段不能跨成员表搜索用户姓名和邮箱。该接口每次使用实时数据库重新校验成员关系；不信任前端组织 ID 或会话中的活动组织 ID，也不使用 Hyperdrive 缓存连接查询权限。

成员目录对组织内成员开放，只返回成员 ID、角色及用户 ID / 姓名 / 邮箱，不返回密码、凭据或其他账号字段。搜索只作用于授权组织，分页采用加入时间和成员 ID 的稳定排序；`%`、`_` 和反斜杠按普通字符搜索。查询参数限制搜索文本长度和页码范围。

## 活动组织如何选择

新建会话时，`findInitialOrganization()` 按加入时间升序选择用户最早加入的组织；时间相同时按成员 ID 排序。没有成员关系时，活动组织为空。当前选择保存在当前服务端会话，重新登录仍按上述初始规则选择，不承诺跨会话记住上次工作区。

注册账号不会自动创建组织。成员页使用随机后缀生成唯一标识，同名团队和中文名称都可创建；创建成功后等待会话刷新，再显示新团队成员。创建失败保留表单和当前工作区，并显示服务端错误。

切换到个人工作区向插件提交 `organizationId: null`。只有服务端响应后才刷新界面，不先在本地假设切换成功。插件在拒绝已失去成员关系的工作区时可能清空活动组织，因此切换失败也要重新读取会话。侧栏保留错误提示；不可用团队可通过选择个人工作区恢复，组织列表加载失败可点击重试。

查询键包含用户、组织、搜索文本和页码。不同团队及不同搜索页面不复用结果；切换后回到第 1 页并清空成员搜索。工作区创建与切换串行执行，避免两个操作同时修改服务端会话。

## 组织与个人账单

存在活动组织时按组织计费，否则按用户个人计费。套餐查询使用实时 `ctx.db` 校验当前成员；订阅变更由 Stripe 插件的 `authorizeReference` 再校验所有者或管理员角色。

切换时取消旧账单请求、使账单缓存失效，再刷新会话和活动账单查询。会话查询在原缓存条目上更新，保持已挂载页面的订阅连接；不能直接移除正在使用的会话查询，否则侧栏和正文可能读取不同的会话状态。成员页不会用某个团队的套餐缓存展示另一个团队的状态。真实 Stripe 收费和回调仍需单独沙箱验收。

## 排查与验收

1. 无团队账号打开成员页：显示个人工作区说明和创建表单。
2. 创建两个团队：分别成为活动团队，两者都出现在侧栏选择器中。
3. 在两支团队与个人工作区之间切换：选择器、成员目录和账单范围保持一致；历史和积分仍为本人数据。
4. 搜索位于第二页的成员：第 1 页搜索结果能找到，不只搜索当前已加载数据。
5. 超过 20 名成员：上一页 / 下一页覆盖全部成员，无重复；无匹配项显示空状态。
6. 使用他人组织 ID 或已撤销成员关系：搜索与切换由服务端拒绝；旧会话不授予访问权。
7. 切换失败：显示错误并重新同步会话，可回个人工作区；创建达到上限时显示错误且不改变当前工作区。

自动化检查涵盖真实数据库搜索、分页、成员隔离、撤销后的访问、特殊搜索字符、工作区切换接口与客户端缓存恢复。Cloudflare 部署、邀请投递和 Stripe 沙箱不属于这次已验证范围。
