# 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"(两段式上传自动转正) ```