Sprint 2 接口文档 — 用户群组管理
Phase 2.5 API-First:先定接口,再写代码。
版本:v1.0 Draft | 日期:2026-03-13 | 作者:小虎
0. 约定
通用响应格式
// 成功
{ "code": 0, "data": { ... } }
// 失败
{ "code": 10001, "message": "错误描述" }
错误码定义
| 码 | 含义 | 说明 |
|---|---|---|
| 0 | 成功 | — |
| 10001 | 参数错误 | 请求体校验失败 |
| 10002 | 未登录 | 缺少或无效 Bearer Token |
| 10003 | 权限不足 | 当前用户无权执行此操作 |
| 10004 | 资源不存在 | 目标用户/群组/权限不存在 |
| 10005 | 账号或密码错误 | 登录专用 |
| 10006 | 账号不可用 | 被禁用或已删除 |
| 10007 | 密码强度不足 | 至少8位,含大小写+数字 |
| 10008 | 用户名已存在 | 创建/修改用户时重复 |
| 10009 | 群组 key 已存在 | 创建/修改群组时重复 |
| 10010 | 不允许提权 | 不能创建/修改更高角色 |
| 10011 | 不允许操作自己 | 不能删除/禁用自己 |
| 10012 | 默认群组不可删 | is_default=1 的群组 |
认证
所有接口(除 /auth/login)需在 Header 中携带:
Authorization: Bearer <access_token>
分页
通用分页参数(Query String):
| 参数 | 类型 | 默认 | 说明 |
|---|---|---|---|
| page | int | 1 | 页码,从 1 开始 |
| page_size | int | 20 | 每页条数,最大 100 |
通用分页响应:
{
"items": [...],
"total": 120,
"page": 1,
"page_size": 20
}
角色层级
superadmin > admin > user
superadmin:超级管理员,免群组鉴权,拥有所有权限admin:管理员,可管理 user 级别用户和群组user:普通用户,只能查看/修改自己的信息
防提权原则:不能创建/修改与自己同级或更高的 role_type。
1. 用户管理(T-BE-07)
1.1 获取用户列表
GET /api/v1/users
权限:admin+ | 权限 key:user:list
Query 参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| page | int | 否 | 页码 |
| page_size | int | 否 | 每页条数 |
| keyword | string | 否 | 搜索(模糊匹配 username / nickname / email) |
| status | int | 否 | 0=禁用 1=启用 |
| role_type | string | 否 | 筛选角色类型 |
| group_id | int | 否 | 筛选群组 |
响应:
{
"code": 0,
"data": {
"items": [
{
"user_id": 1,
"username": "tom",
"nickname": "Tom",
"email": "tom@example.com",
"phone": "13800138000",
"avatar": "",
"role_type": "admin",
"status": 1,
"last_login_at": 1773400000,
"last_login_ip": "192.168.1.1",
"groups": [
{ "group_id": 1, "group_name": "运营组" }
],
"created_at": "2026-03-01T00:00:00Z"
}
],
"total": 50,
"page": 1,
"page_size": 20
}
}
注意:
- 不返回
password字段 user角色只能看到自己(返回单条)- admin 不能看到 superadmin 的列表
- 按
create_time DESC排序
1.2 获取用户详情
GET /api/v1/users/:id
权限:admin+ | 自己可查自己 | 权限 key:user:read
响应:同 1.1 单条,额外包含:
{
"code": 0,
"data": {
"user_id": 2,
"username": "alice",
"nickname": "Alice",
"email": "alice@example.com",
"phone": "13800138001",
"avatar": "",
"role_type": "user",
"status": 1,
"last_login_at": 1773400000,
"last_login_ip": "192.168.1.2",
"groups": [
{ "group_id": 1, "group_name": "运营组" },
{ "group_id": 2, "group_name": "财务组" }
],
"permissions": ["user:list", "order:read"],
"created_at": "2026-03-01T00:00:00Z",
"updated_at": "2026-03-10T00:00:00Z"
}
}
1.3 创建用户
POST /api/v1/users
权限:admin+ | 权限 key:user:create
请求体:
{
"username": "bob",
"password": "StrongPwd123",
"nickname": "Bob",
"email": "bob@example.com",
"phone": "13800138002",
"role_type": "user",
"status": 1,
"group_ids": [1, 2]
}
校验规则:
| 字段 | 规则 |
|---|---|
| username | 必填,3-64 字符,唯一 |
| password | 必填,≥8 位,含大小写+数字 |
| nickname | 必填,1-64 字符 |
| 必填,合法邮箱格式,唯一 | |
| phone | 可选 |
| role_type | 必填,只能是低于当前用户的角色 |
| group_ids | 可选,整数数组 |
响应:
{
"code": 0,
"data": {
"user_id": 3
}
}
防提权:
- admin 只能创建
user - superadmin 可以创建
admin和user - 不能创建
superadmin(系统唯一)
1.4 更新用户
PUT /api/v1/users/:id
权限:admin+ | 权限 key:user:update
请求体(全量更新,不传的字段保持不变):
{
"nickname": "Bob Updated",
"email": "bob_new@example.com",
"phone": "13900139000",
"role_type": "user",
"group_ids": [1, 3]
}
注意:
- 不能通过此接口修改密码(走专用改密接口)
- 不能通过此接口修改
username(创建后不可变) - 修改
role_type触发防提权校验 - 修改
group_ids全量替换(先删后增) - 修改成功后
auth_version + 1,使已有 session 失效
1.5 删除用户(软删除)
DELETE /api/v1/users/:id
权限:admin+ | 权限 key:user:delete
响应:
{ "code": 0, "data": { "deleted": true } }
规则:
- 软删除:
is_deleted = 1 - 不能删自己(10011)
- admin 不能删 admin 和 superadmin(10010)
- 删除后
auth_version + 1,踢出所有 session - 同时清理
sys_user_group关联
1.6 启用/禁用用户
PATCH /api/v1/users/:id/status
权限:admin+ | 权限 key:user:update
请求体:
{ "status": 0 }
1= 启用,0= 禁用- 禁用后
auth_version + 1,踢出所有 session - 不能禁用自己(10011)
1.7 管理员重置密码
PATCH /api/v1/users/:id/password
权限:admin+ | 权限 key:user:reset-password
请求体:
{ "new_password": "NewStrongPwd456" }
规则:
- 密码强度校验(同创建用户)
- 成功后
auth_version + 1,踢出目标用户所有 session - admin 不能重置 admin/superadmin 的密码(10010)
2. 群组管理(T-BE-09)
2.1 获取群组列表
GET /api/v1/groups
权限:admin+ | 权限 key:group:list
Query 参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| page | int | 否 | 页码 |
| page_size | int | 否 | 每页条数 |
| keyword | string | 否 | 搜索 group_name |
| status | int | 否 | 0=禁用 1=启用 |
响应:
{
"code": 0,
"data": {
"items": [
{
"group_id": 1,
"group_name": "运营组",
"group_key": "ops",
"description": "运营团队",
"is_default": 0,
"status": 1,
"member_count": 12,
"permission_count": 8,
"created_at": "2026-03-01T00:00:00Z"
}
],
"total": 5,
"page": 1,
"page_size": 20
}
}
2.2 获取群组详情
GET /api/v1/groups/:id
权限:admin+ | 权限 key:group:read
响应:同 2.1 单条,额外包含 permissions 和 members 摘要。
2.3 创建群组
POST /api/v1/groups
权限:admin+ | 权限 key:group:create
请求体:
{
"group_name": "财务组",
"group_key": "finance",
"description": "财务团队",
"status": 1,
"sort_order": 0,
"permission_ids": [1, 2, 3]
}
校验:
| 字段 | 规则 |
|---|---|
| group_name | 必填,1-64 字符 |
| group_key | 必填,1-64 字符,唯一,推荐英文 snake_case |
| description | 可选,最多 256 字符 |
| permission_ids | 可选,只能分配自己拥有的权限(防提权) |
2.4 更新群组
PUT /api/v1/groups/:id
权限:admin+ | 权限 key:group:update
请求体:
{
"group_name": "财务组(新)",
"description": "更新描述",
"status": 1,
"sort_order": 10
}
注意:
group_key创建后不可修改- 修改群组后,群组内所有用户
auth_version + 1
2.5 删除群组
DELETE /api/v1/groups/:id
权限:admin+ | 权限 key:group:delete
规则:
- 默认群组(
is_default=1)不可删(10012) - 软删除:
is_deleted = 1 - 同时清理
sys_user_group和sys_group_permission关联 - 群组内所有用户
auth_version + 1
3. 群组权限分配(T-BE-10)
3.1 获取群组权限
GET /api/v1/groups/:id/permissions
权限:admin+ | 权限 key:group:read
响应:
{
"code": 0,
"data": {
"group_id": 1,
"permissions": [
{
"perm_id": 1,
"permission_key": "user:list",
"permission_name": "用户列表",
"module": "user"
},
{
"perm_id": 2,
"permission_key": "user:read",
"permission_name": "用户详情",
"module": "user"
}
]
}
}
3.2 设置群组权限(全量替换)
PUT /api/v1/groups/:id/permissions
权限:admin+ | 权限 key:group:assign-permission
请求体:
{
"permission_ids": [1, 2, 3, 5]
}
规则:
- 全量替换:先删除旧关联,再插入新关联
- 防提权:admin 只能分配自己拥有的权限(superadmin 不受限)
- 成功后群组内所有用户
auth_version + 1
4. 群组成员管理(T-BE-11)
4.1 获取群组成员
GET /api/v1/groups/:id/members
权限:admin+ | 权限 key:group:read
Query 参数:分页 + keyword(搜索用户名/昵称)
响应:
{
"code": 0,
"data": {
"items": [
{
"user_id": 2,
"username": "alice",
"nickname": "Alice",
"email": "alice@example.com",
"role_type": "user",
"status": 1,
"joined_at": "2026-03-05T00:00:00Z"
}
],
"total": 12,
"page": 1,
"page_size": 20
}
}
4.2 批量添加成员
POST /api/v1/groups/:id/members
权限:admin+ | 权限 key:group:manage-member
请求体:
{
"user_ids": [2, 3, 5]
}
规则:
- 已在群组内的用户自动跳过(幂等)
- 添加成功后目标用户
auth_version + 1
4.3 移除成员
DELETE /api/v1/groups/:id/members/:userId
权限:admin+ | 权限 key:group:manage-member
规则:
- 移除后目标用户
auth_version + 1
5. 权限列表(辅助接口)
5.1 获取所有权限(按 module 分组)
GET /api/v1/permissions
权限:admin+ | 权限 key:permission:list
响应:
{
"code": 0,
"data": {
"modules": [
{
"module": "user",
"permissions": [
{ "perm_id": 1, "permission_key": "user:list", "permission_name": "用户列表" },
{ "perm_id": 2, "permission_key": "user:read", "permission_name": "用户详情" },
{ "perm_id": 3, "permission_key": "user:create", "permission_name": "创建用户" },
{ "perm_id": 4, "permission_key": "user:update", "permission_name": "更新用户" },
{ "perm_id": 5, "permission_key": "user:delete", "permission_name": "删除用户" },
{ "perm_id": 6, "permission_key": "user:reset-password", "permission_name": "重置密码" }
]
},
{
"module": "group",
"permissions": [
{ "perm_id": 7, "permission_key": "group:list", "permission_name": "群组列表" },
{ "perm_id": 8, "permission_key": "group:read", "permission_name": "群组详情" },
{ "perm_id": 9, "permission_key": "group:create", "permission_name": "创建群组" },
{ "perm_id": 10, "permission_key": "group:update", "permission_name": "更新群组" },
{ "perm_id": 11, "permission_key": "group:delete", "permission_name": "删除群组" },
{ "perm_id": 12, "permission_key": "group:assign-permission", "permission_name": "分配权限" },
{ "perm_id": 13, "permission_key": "group:manage-member", "permission_name": "管理成员" }
]
}
]
}
}
6. 资源可见性过滤(T-BE-12)
不是独立接口,而是横切逻辑。
规则
| 角色 | 可见范围 |
|---|---|
| superadmin | 所有资源,无限制 |
| admin | 所有 user 级别用户 + 自己创建的群组 + 被分配的群组 |
| user | 只能看到自己的信息 |
实现方式
- 在所有 LIST / GET 接口的 SQL 查询中注入角色过滤条件
- 建议封装为公共方法:
ApplyVisibilityFilter(query, identity) query - superadmin 跳过过滤
- admin 过滤掉
role_type = 'superadmin'的用户 - user 固定
WHERE user_id = ?
7. auth_version 递增 + 缓存失效(T-BE-13)
不是独立接口,而是写操作的副作用。
触发 auth_version + 1 的操作
| 操作 | 影响范围 |
|---|---|
| 修改用户信息 | 该用户 |
| 重置密码 | 该用户 |
| 禁用/删除用户 | 该用户 |
| 修改群组权限 | 群组内所有用户 |
| 删除群组 | 群组内所有用户 |
| 用户加入/移出群组 | 该用户 |
流程
- 业务操作 → DB 更新
auth_version + 1(SQLUPDATE sys_user SET auth_version = auth_version + 1 WHERE user_id IN (...))- 清除 Redis 中受影响用户的 session(
DEL session:<userId>:*) - 被踢用户下次请求会因 session 不存在而 401
8. Redis Session 删除补偿(T-BE-14)
不是独立接口,而是后台逻辑。
场景
Redis 清除 session 时可能因网络抖动失败,导致被踢用户仍能访问。
方案
在 AuthRequired 中间件中增加版本校验:
1. 从 JWT 解析 uid
2. 从 Redis 获取 session
3. 从 DB 读取当前 auth_version(可 Redis 缓存,TTL 30s)
4. 对比 JWT 签发时的 auth_version vs 当前值
5. 不匹配 → 返回 401 + 清除该 session
这样即使 Redis 删除失败,中间件也能兜底拦截。
9. 接口总览
| # | 方法 | 路径 | 权限 key | 任务 |
|---|---|---|---|---|
| 1 | GET | /api/v1/users | user:list | T-BE-07 |
| 2 | GET | /api/v1/users/:id | user:read | T-BE-07 |
| 3 | POST | /api/v1/users | user:create | T-BE-07 |
| 4 | PUT | /api/v1/users/:id | user:update | T-BE-07 |
| 5 | DELETE | /api/v1/users/:id | user:delete | T-BE-07 |
| 6 | PATCH | /api/v1/users/:id/status | user:update | T-BE-07 |
| 7 | PATCH | /api/v1/users/:id/password | user:reset-password | T-BE-07 |
| 8 | GET | /api/v1/groups | group:list | T-BE-09 |
| 9 | GET | /api/v1/groups/:id | group:read | T-BE-09 |
| 10 | POST | /api/v1/groups | group:create | T-BE-09 |
| 11 | PUT | /api/v1/groups/:id | group:update | T-BE-09 |
| 12 | DELETE | /api/v1/groups/:id | group:delete | T-BE-09 |
| 13 | GET | /api/v1/groups/:id/permissions | group:read | T-BE-10 |
| 14 | PUT | /api/v1/groups/:id/permissions | group:assign-permission | T-BE-10 |
| 15 | GET | /api/v1/groups/:id/members | group:read | T-BE-11 |
| 16 | POST | /api/v1/groups/:id/members | group:manage-member | T-BE-11 |
| 17 | DELETE | /api/v1/groups/:id/members/:userId | group:manage-member | T-BE-11 |
| 18 | GET | /api/v1/permissions | permission:list | 辅助 |
总计:18 个接口
10. 权限 Seed 数据
Sprint 2 上线时需要初始化 sys_permission 表:
INSERT INTO sys_permission (permission_key, permission_name, module, description, status, create_time) VALUES
-- user 模块
('user:list', '用户列表', 'user', '查看用户列表', 1, UNIX_TIMESTAMP()),
('user:read', '用户详情', 'user', '查看用户详情', 1, UNIX_TIMESTAMP()),
('user:create', '创建用户', 'user', '创建新用户', 1, UNIX_TIMESTAMP()),
('user:update', '更新用户', 'user', '修改用户信息', 1, UNIX_TIMESTAMP()),
('user:delete', '删除用户', 'user', '删除用户', 1, UNIX_TIMESTAMP()),
('user:reset-password', '重置密码', 'user', '管理员重置用户密码', 1, UNIX_TIMESTAMP()),
-- group 模块
('group:list', '群组列表', 'group', '查看群组列表', 1, UNIX_TIMESTAMP()),
('group:read', '群组详情', 'group', '查看群组详情及成员', 1, UNIX_TIMESTAMP()),
('group:create', '创建群组', 'group', '创建新群组', 1, UNIX_TIMESTAMP()),
('group:update', '更新群组', 'group', '修改群组信息', 1, UNIX_TIMESTAMP()),
('group:delete', '删除群组', 'group', '删除群组', 1, UNIX_TIMESTAMP()),
('group:assign-permission', '分配权限', 'group', '为群组分配权限', 1, UNIX_TIMESTAMP()),
('group:manage-member', '管理成员', 'group', '添加/移除群组成员', 1, UNIX_TIMESTAMP()),
-- permission 模块
('permission:list', '权限列表', 'permission', '查看所有权限', 1, UNIX_TIMESTAMP());
11. 技术评审决策(2026-03-13 大猫确认)
| # | 决策项 | 结论 | 理由 |
|---|---|---|---|
| 1 | username 可否修改 | 不可修改 | 登录凭据 + 审计关联字段,改了日志追溯断裂;显示名用 nickname |
| 2 | superadmin API 创建 | 不允许 | 只能 DB 直插,系统唯一,降低提权风险 |
| 3 | 群组删除级联 sys_api | 不清理 | sys_api 是全局定义,生命周期跟权限绑定,不跟群组走 |
| 4 | auth_version 校验位置 | 中间件 | 横切关注点,与 T-BE-14 Redis 补偿融合,一处搞定 |
| 5 | 批量操作接口 | Sprint 2 不做 | 用户量小,YAGNI,等前端提需求再加 |
| 6 | 操作日志 AOP | Sprint 3 做 | 禅道 Sprint 3 专项,DB 表已就位,Sprint 2 先跑通业务 |