91 lines
3.7 KiB
Markdown
91 lines
3.7 KiB
Markdown
# 6.1 用户管理
|
||
|
||
:::tip 概述
|
||
用户模块(goframe_user,租户级表)是机制最全的模块:唯一校验(按行现租户)、租户用户上限、角色全量重写、城市编解码、Excel 导入导出、数据权限接线、批量名称回填。路由前缀 /api/user。
|
||
:::
|
||
|
||
## 接口清单
|
||
|
||
| 方法 | 路由 | 权限 | 说明 |
|
||
|------|------|------|------|
|
||
| GET | /api/user/page | sys:user:page | 分页(关键词/状态/部门/角色/职级/岗位筛选) |
|
||
| GET | /api/user/list | sys:user:list | 全量列表(下拉选择用) |
|
||
| GET | /api/user/detail/:id | sys:user:detail | 详情(含角色 ID 数组与四级区划编码) |
|
||
| POST | /api/user/add | sys:user:add | 新增(返回新记录 ID) |
|
||
| PUT | /api/user/update | sys:user:update | 修改(密码留空不改) |
|
||
| PUT | /api/user/status | sys:user:status | 状态切换 |
|
||
| PUT | /api/user/resetPwd | sys:user:resetPwd | 重置密码(logType=reset) |
|
||
| DELETE | /api/user/delete/:id | sys:user:delete | 软删(清理 user_role) |
|
||
| DELETE | /api/user/batchDelete | sys:user:batchDelete | 批量删除 |
|
||
| POST | /api/user/import | sys:user:import | Excel 导入 |
|
||
| GET | /api/user/export | sys:user:export | Excel 导出 |
|
||
| GET | /api/user/document/:id | sys:user:detail | 打印信息 |
|
||
|
||
## 核心机制
|
||
|
||
### 唯一校验(手写,按行现租户)
|
||
|
||
```text
|
||
usernameExists / mobileExists 按 (username|mobile, tenant_id, 排除自身) 查;
|
||
Update 时先读被改行的 tenant_id 再校验——校验范围必须等于
|
||
uk_tenant_username / uk_tenant_mobile 索引范围(含 del_flag 由 dao.Ctx 恒过滤保证)。
|
||
|
||
历史教训:用 GetTenantID(超管=默认租户)会漏报 1062——必须按行现租户。
|
||
```
|
||
|
||
### 角色同步(AssignRoles)
|
||
|
||
```text
|
||
AssignRoles(userId, roleIds):全量重写 user_role(先删后插)
|
||
→ clearIdentityCache(userId)
|
||
Add/Update 均经此方法;Update 改 dept_id 也在该路径清缓存——部门变更即时生效
|
||
(数据权限无需等 300s 缓存过期)
|
||
```
|
||
|
||
### 租户用户上限(checkTenantUserMax)
|
||
|
||
统计租户现有用户数 vs tenant.max_user_num,超限返回 1007。在 user.Add 生效。
|
||
|
||
### 用户编码(genUserCode)
|
||
|
||
```text
|
||
前缀=租户 code(平台/超管为 "SYS"),序号=该租户现有用户数+1
|
||
格式 "{prefix}-%06d",如 T0001-000001
|
||
```
|
||
|
||
### 数据权限接线
|
||
|
||
userSpec 声明 `DataScope: ApplyDataScope("dept_id")`——user 是当前唯一接线模块(全库仅 user 表有 dept_id 列)。
|
||
|
||
### 列表名称回填(userExpandNames)
|
||
|
||
列表行批量补 dept/level/position/role 名称与城市文本(namesById/dictName 批查防 N+1)。
|
||
|
||
## 导入导出
|
||
|
||
```text
|
||
Import(POST /api/user/import):
|
||
excel.ReadSheet → 表头映射 → 逐行:唯一校验 / 租户上限 / 默认密码 bcrypt
|
||
(事务外预计算)/ 角色与城市解析 → 批量插入 → 返回成功/失败统计
|
||
城市列经 ResolveCityCodes(名称串) 转编码入库
|
||
|
||
Export(GET /api/user/export):
|
||
与列表同筛选(userFilter + DataScope,applyFilters 同链)
|
||
→ ChunkQuery 500/页 → excel.WriteSheet
|
||
导出可见集 = 列表可见集(导出=所见)
|
||
```
|
||
|
||
## 城市编解码接入
|
||
|
||
Add/Update/Import/Detail 均接入包级函数:写侧 `ResolveCityCodes`(名称串 → 四级编码入库),读侧 `ResolveCityNames`(编码 → 名称还原)。机制见 [6.9 行政区划管理](./city)。
|
||
|
||
## 字段与安全边界
|
||
|
||
```text
|
||
password/salt:bcrypt 双重加盐;接口永不返回
|
||
密码留空 = 不修改(Update 语义)
|
||
删除守卫:userDeleteGuard / 关联清理(user_role)
|
||
SerializeMaps:status → user_status({field}Text 字典填充)
|
||
FileFields: avatar + FileSaveDir: "user"(两段式上传自动转正)
|
||
```
|