用户管理
用户管理
业务用途
用户管理是系统管理的入口模块,负责维护平台的登录账号。每个用户归属一个部门(deptId),可关联多个岗位(sys_user_post)和多个可访问应用(sys_user_app),并通过 sys_user_role 关联多个角色来获得权限。
平台采用多租户设计:所有查询都会带上 tenantId 过滤,用户列表、用户新建都限定在当前租户范围内。密码统一使用 Spring Security 的 PasswordEncoder(BCrypt)加密存储,新增用户未填密码时默认 123456。
关于导入导出
当前 SysUserController 仅提供列表、详情、增删改、重置密码、改状态等接口,尚未实现批量导入 / 导出 Excel 端点。如需导入功能,可参考 frontend/src/views/system/user/index.vue 逐条新增,或自行扩展 /api/system/user/import 端点。
涉及文件
| 层 | 文件路径 |
|---|---|
| 控制器 | backend/src/main/java/com/lowcode/controller/system/SysUserController.java |
| 服务接口 | backend/src/main/java/com/lowcode/service/ISysUserService.java |
| 服务实现 | backend/src/main/java/com/lowcode/service/impl/SysUserServiceImpl.java |
| 实体 | backend/src/main/java/com/lowcode/entity/SysUser.java |
| 关联实体 | backend/src/main/java/com/lowcode/entity/SysUserPost.java、SysUserRole.java、SysUserApp.java |
| 基类 | backend/src/main/java/com/lowcode/common/BaseEntity.java |
| 前端 API | frontend/src/api/user.ts |
| 前端页面 | frontend/src/views/system/user/index.vue |
数据库表
sys_user(用户表)
| 字段 | 类型 | 说明 |
|---|---|---|
id | Long | 主键,自增(@TableId(IdType.AUTO)) |
tenant_id | Long | 租户 ID(多租户隔离) |
username | String | 用户名(登录账号,租户内唯一) |
password | String | 密码(BCrypt 加密) |
nickname | String | 昵称 |
remark | String | 备注 |
dept_id | Long | 部门 ID |
post_ids | String | 岗位编号数组(历史遗留字段,实际关联走 sys_user_post) |
avatar | String | 头像 URL |
email | String | 邮箱 |
mobile | String | 手机号 |
phone | String | 电话 |
sex | String | 性别(0未知 1男 2女) |
status | String | 状态(0正常 1禁用) |
login_ip | String | 最后登录 IP |
login_date | LocalDateTime | 最后登录时间 |
create_time / update_time | LocalDateTime | 创建/更新时间(自动填充) |
create_by / update_by | String | 创建人/更新人(自动填充) |
deleted | Integer | 逻辑删除(0未删除 1已删除,@TableLogic) |
BaseEntity 公共字段
平台所有实体继承 BaseEntity,自动拥有 id、tenantId、createTime、updateTime、createBy、updateBy、deleted 七个字段。后文各表不再重复列出这些公共字段,只列出业务字段。
关联表
| 表 | 字段 | 说明 |
|---|---|---|
sys_user_role | userId、roleId | 用户-角色多对多 |
sys_user_post | userId、postId | 用户-岗位多对多 |
sys_user_app | userId、appId | 用户可访问应用 |
后端实现
端点一览
SysUserController 基础路径:/api/system/user
| HTTP 方法 | 路径 | 方法名 | 说明 |
|---|---|---|---|
| GET | /api/system/user/list | list | 分页查询用户列表(支持 username/phone/status/deptId 过滤) |
| GET | /api/system/user/{id} | getById | 获取用户详情 |
| GET | /api/system/user/{id}/postIds | getUserPostIds | 获取用户岗位 ID 列表 |
| GET | /api/system/user/{id}/appIds | getUserAppIds | 获取用户可访问应用 ID 列表 |
| GET | /api/system/user/me/appIds | getCurrentUserAppIds | 获取当前登录用户可访问应用 ID 列表 |
| POST | /api/system/user | add | 新增用户(含岗位、应用关联) |
| PUT | /api/system/user | update | 修改用户(含岗位、应用关联,不改密码) |
| DELETE | /api/system/user/{id} | delete | 删除用户 |
| PUT | /api/system/user/resetPwd | resetPwd | 重置密码 |
| PUT | /api/system/user/changeStatus | changeStatus | 修改用户状态(启用/禁用) |
关键代码
列表查询带租户隔离与多条件过滤,并使用 @PreAuthorize 做按钮级权限校验:
@GetMapping("/list")
@PreAuthorize("hasAuthority('system:user:list')")
public Result<Page<SysUser>> list(
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(required = false) String username,
@RequestParam(required = false) String phone,
@RequestParam(required = false) String status,
@RequestParam(required = false) Long deptId,
HttpServletRequest request) {
Long tenantId = getTenantId(request);
LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysUser::getTenantId, tenantId);
wrapper.like(StringUtils.hasText(username), SysUser::getUsername, username);
wrapper.like(StringUtils.hasText(phone), SysUser::getPhone, phone);
wrapper.eq(StringUtils.hasText(status), SysUser::getStatus, status);
wrapper.eq(deptId != null, SysUser::getDeptId, deptId);
wrapper.orderByDesc(SysUser::getCreateTime);
return Result.success(PageHelper.doPage(userService, wrapper, pageNum, pageSize));
}新增用户时密码用 PasswordEncoder 加密,未填则默认 123456,并通过 createUser 事务方法一并保存岗位、应用关联:
@PostMapping
public Result<Void> add(@RequestBody UserCreateRequest request, HttpServletRequest httpRequest) {
SysUser user = request.getUser();
user.setPostIds(null); // 清除遗留字段,关联走 sys_user_post
if (user.getTenantId() == null) {
user.setTenantId(getTenantId(httpRequest));
}
if (userService.existsByUsername(user.getUsername())) {
return Result.error("用户名已存在");
}
// 默认密码 123456
if (!StringUtils.hasText(user.getPassword())) {
user.setPassword(passwordEncoder.encode("123456"));
} else {
user.setPassword(passwordEncoder.encode(user.getPassword()));
}
if (user.getStatus() == null || user.getStatus().isEmpty()) {
user.setStatus("0");
}
userService.createUser(user, request.getPostIds(), request.getAppIds());
return Result.success("新增成功");
}SysUserServiceImpl.createUser 使用 @Transactional 保证用户主表与关联表的一致性——先保存用户,再批量插入岗位关联和应用关联;更新时先删后插重建关联;删除时一并清理关联记录:
@Override
@Transactional(rollbackFor = Exception.class)
public void createUser(SysUser user, List<Long> postIds, List<Long> appIds) {
save(user);
if (!CollectionUtils.isEmpty(postIds)) {
saveUserPosts(user.getId(), postIds);
}
saveUserApps(user.getId(), user.getTenantId(), appIds);
}请求体结构
新增/修改使用嵌套请求体,把用户主体与关联 ID 数组分开传:
{
"user": {
"username": "zhangsan",
"nickname": "张三",
"deptId": 100,
"status": "0"
},
"postIds": [1, 2],
"appIds": [10, 11]
}重置密码 ResetPwdRequest:{ "userId": 1, "password": "newpass" } 改状态 ChangeStatusRequest:{ "userId": 1, "status": "0" }
前端实现
- API 层
frontend/src/api/user.ts:封装了getUserList、getUser、addUser、updateUser、deleteUser、resetUserPwd、changeUserStatus七个方法,统一调用/system/user/*端点。 - 页面
frontend/src/views/system/user/index.vue:基于 Element Plus 的el-table+el-pagination实现列表,弹窗表单做新增/编辑,支持按用户名、手机号、状态筛选。重置密码、启用/禁用通过el-switch或按钮触发二次确认。

// frontend/src/api/user.ts
export function addUser(data: any) {
return request.post('/system/user', data)
}
export function resetUserPwd(data: { userId: number; password: string }) {
return request.put('/system/user/resetPwd', data)
}操作步骤
- 登录平台,进入「系统管理 → 用户管理」。
- 点击「新增」,填写用户名、昵称、部门、岗位(多选)、可访问应用(多选);密码留空则默认
123456。

- 列表中点击「编辑」修改资料,点击「重置密码」给用户设新密码,点击状态开关启用/禁用账号。

- 通过用户名、手机号、状态、部门筛选查询。
常见问题
新增用户提示「用户名已存在」?
用户名在同一租户内必须唯一(existsByUsername 按 tenantId 过滤)。请换一个用户名,或确认是否在其他租户已存在同名账号。
修改用户时密码会被改掉吗?
不会。update 方法显式 user.setPassword(null),更新时不触碰密码字段。改密码请用「重置密码」功能。
删除用户后关联的角色/岗位会清理吗?
会。deleteUser 事务方法在 removeById 之后会调用 deleteUserPosts 和 deleteUserApps 清理关联表。角色关联 sys_user_role 需在角色侧维护。
为什么列表查询要带 tenantId?
平台是多租户架构,每个租户的用户相互隔离。tenantId 从 JWT Token 中解析(getTenantId),未登录时回退到默认租户 1。
