feat(admin): 实现用户管理列表及新增用户功能

- 新增用户列表页面,实现分页查询和条件筛选
- 增加新增用户弹窗表单,支持姓名、手机号及密码录入和校验
- 后端新增 AdminController 提供用户列表查询和创建接口
- 完善 UserService 和 RoleService,支持分页用户数据获取及用户角色映射
- 丰富数据库 Mapper 增加用户及用户角色相关查询插入操作
- 定时任务 UserRoleTask 调整调用角色服务更新权限缓存
- 前端接口封装新建用户相关请求便于调用
- 使用密码加密存储新建用户密码保障安全
This commit is contained in:
lbw
2025-12-24 11:25:27 +08:00
parent 5404f295e4
commit 4135b72648
19 changed files with 424 additions and 90 deletions

View File

@@ -0,0 +1,69 @@
package com.yinlihupo.enlish.service.controller;
import com.yinlihupo.enlish.service.domain.dataobject.RoleDO;
import com.yinlihupo.enlish.service.domain.dataobject.UserDO;
import com.yinlihupo.enlish.service.model.vo.user.CreateUserReqVO;
import com.yinlihupo.enlish.service.model.vo.user.FindUserListRepVO;
import com.yinlihupo.enlish.service.model.vo.user.FindUserListRspVO;
import com.yinlihupo.enlish.service.service.RoleService;
import com.yinlihupo.enlish.service.service.UserService;
import com.yinlihupo.framework.biz.operationlog.aspect.ApiOperationLog;
import com.yinlihupo.framework.common.response.PageResponse;
import com.yinlihupo.framework.common.response.Response;
import jakarta.annotation.Resource;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
@RequestMapping("/admin/")
@RestController
public class AdminController {
@Resource
private UserService userService;
@Resource
private PasswordEncoder passwordEncoder;
@Resource
private RoleService roleService;
@PostMapping("user/list")
@ApiOperationLog(description = "查询用户列表")
public PageResponse<FindUserListRspVO> listUser(@RequestBody FindUserListRepVO findUserListRepVO) {
Integer userTotal = userService.findUserTotal();
if (userTotal == null || userTotal == 0) {
throw new RuntimeException("暂无用户");
}
Integer page = findUserListRepVO.getPage();
Integer pageSize = findUserListRepVO.getPageSize();
String name = findUserListRepVO.getName();
List<UserDO> usersList = userService.findUsersList(page, pageSize, name.isEmpty() ? null : name);
Map<Long, RoleDO> userId2RoleMap = roleService.findUserId2RoleMap(usersList.stream().map(UserDO::getId).toList());
return PageResponse.success(usersList.stream().map(userDO -> FindUserListRspVO.builder()
.id(userDO.getId())
.name(userDO.getName())
.phone(userDO.getPhone())
.roleName(userId2RoleMap.get(userDO.getId()).getRoleName())
.build()).toList(), page, userTotal, pageSize);
}
@PostMapping("user/create")
public Response<Void> createUser(@RequestBody CreateUserReqVO createUserReqVO) {
String name = createUserReqVO.getName();
String phone = createUserReqVO.getPhone();
String password = createUserReqVO.getPassword();
userService.createUser(UserDO.builder()
.name(name)
.phone(phone)
.password(passwordEncoder.encode(password))
.build());
return Response.success();
}
}

View File

@@ -22,8 +22,4 @@ public class UserDO {
private String email;
private Integer status;
private Integer isDeleted;
}

View File

@@ -1,7 +1,16 @@
package com.yinlihupo.enlish.service.domain.dataobject;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
public class UserRoleRelDO {
private Long id;
@@ -11,45 +20,4 @@ public class UserRoleRelDO {
private Date createTime;
private Boolean isDeleted;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public Long getRoleId() {
return roleId;
}
public void setRoleId(Long roleId) {
this.roleId = roleId;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Boolean getIsDeleted() {
return isDeleted;
}
public void setIsDeleted(Boolean isDeleted) {
this.isDeleted = isDeleted;
}
}

View File

@@ -1,10 +1,13 @@
package com.yinlihupo.enlish.service.domain.mapper;
import com.yinlihupo.enlish.service.domain.dataobject.RoleDO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface RoleDOMapper {
List<RoleDO> selectAll();
List<RoleDO> selectByIds(@Param("ids") List<Long> ids);
}

View File

@@ -1,6 +1,9 @@
package com.yinlihupo.enlish.service.domain.mapper;
import com.yinlihupo.enlish.service.domain.dataobject.UserDO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface UserDOMapper {
@@ -9,4 +12,10 @@ public interface UserDOMapper {
void insert(UserDO userDO);
UserDO selectById(Long id);
List<UserDO> selectUserDOList(@Param("name") String name, @Param("startIndex") Integer startIndex, @Param("pageSize") Integer pageSize);
int selectUserTotal();
void createUser(UserDO userDO);
}

View File

@@ -1,10 +1,13 @@
package com.yinlihupo.enlish.service.domain.mapper;
import com.yinlihupo.enlish.service.domain.dataobject.UserRoleRelDO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface UserRoleRelDOMapper {
List<UserRoleRelDO> selectAll();
List<UserRoleRelDO> selectByUserIds(@Param("userIds") List<Long> userIds);
}

View File

@@ -0,0 +1,17 @@
package com.yinlihupo.enlish.service.model.vo.user;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
public class CreateUserReqVO {
private String name;
private String phone;
private String password;
}

View File

@@ -0,0 +1,17 @@
package com.yinlihupo.enlish.service.model.vo.user;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
public class FindUserListRepVO {
private Integer page;
private Integer pageSize;
private String name;
}

View File

@@ -0,0 +1,17 @@
package com.yinlihupo.enlish.service.model.vo.user;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
public class FindUserListRspVO {
private Long id;
private String name;
private String phone;
private String roleName;
}

View File

@@ -0,0 +1,13 @@
package com.yinlihupo.enlish.service.service;
import com.yinlihupo.enlish.service.domain.dataobject.RoleDO;
import java.util.List;
import java.util.Map;
public interface RoleService {
void pushRolePermission2Redis();
Map<Long, RoleDO> findUserId2RoleMap(List<Long> userIds);
}

View File

@@ -2,9 +2,15 @@ package com.yinlihupo.enlish.service.service;
import com.yinlihupo.enlish.service.domain.dataobject.UserDO;
import java.util.List;
public interface UserService {
void pushRolePermission2Redis();
UserDO findUser();
List<UserDO> findUsersList(int page, int limit, String name);
Integer findUserTotal();
void createUser(UserDO userDO);
}

View File

@@ -0,0 +1,63 @@
package com.yinlihupo.enlish.service.service.role;
import com.yinlihupo.enlish.service.constant.RoleConstants;
import com.yinlihupo.enlish.service.domain.dataobject.RoleDO;
import com.yinlihupo.enlish.service.domain.dataobject.UserRoleRelDO;
import com.yinlihupo.enlish.service.domain.mapper.RoleDOMapper;
import com.yinlihupo.enlish.service.domain.mapper.UserDOMapper;
import com.yinlihupo.enlish.service.domain.mapper.UserRoleRelDOMapper;
import com.yinlihupo.enlish.service.service.RoleService;
import com.yinlihupo.framework.common.util.JsonUtils;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service
@Slf4j
public class RoleServiceImpl implements RoleService {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Resource
private RoleDOMapper roleDOMapper;
@Resource
private UserRoleRelDOMapper userRoleRelDOMapper;
@Override
public void pushRolePermission2Redis() {
List<RoleDO> roleDOS = roleDOMapper.selectAll();
List<String> roleKeys = roleDOS.stream().map(RoleDO::getRoleKey).toList();
log.info("将角色同步到 redis 中, {}", roleKeys);
stringRedisTemplate.opsForValue().set(RoleConstants.ROLE, JsonUtils.toJsonString(roleKeys), 60 * 60 * 24);
Map<Long, RoleDO> roleId2RoleDO = roleDOS.stream().collect(Collectors.toMap(RoleDO::getId, roleDO -> roleDO));
List<UserRoleRelDO> userRoleRelDOS = userRoleRelDOMapper.selectAll();
Map<Long, List<UserRoleRelDO>> userId2UserRoleRelDOs = userRoleRelDOS.stream().collect(Collectors.groupingBy(UserRoleRelDO::getUserId));
userId2UserRoleRelDOs.forEach((userId, userRoleRelDOs) -> {
List<Long> roleIds = userRoleRelDOs.stream().map(UserRoleRelDO::getRoleId).toList();
List<RoleDO> roleDOs = roleIds.stream().map(roleId2RoleDO::get).toList();
List<String> user2RoleKeys = roleDOs.stream().map(RoleDO::getRoleKey).toList();
log.info("将用户 {} 的角色同步到 redis 中, {}", userId, roleKeys);
stringRedisTemplate.opsForValue().set(RoleConstants.buildUserRoleKey(userId), JsonUtils.toJsonString(user2RoleKeys), 60 * 60 * 24);
});
}
@Override
public Map<Long, RoleDO> findUserId2RoleMap(List<Long> userIds) {
List<UserRoleRelDO> userRoleRelDOS = userRoleRelDOMapper.selectByUserIds(userIds);
Map<Long, UserRoleRelDO> userId2UserRoleRelDOs = userRoleRelDOS.stream().collect(Collectors.toMap(UserRoleRelDO::getUserId, userRoleRelDO -> userRoleRelDO));
List<RoleDO> roleDOS = roleDOMapper.selectByIds(userRoleRelDOS.stream().map(UserRoleRelDO::getRoleId).toList());
Map<Long, RoleDO> roleId2RoleDO = roleDOS.stream().collect(Collectors.toMap(RoleDO::getId, roleDO -> roleDO));
return userIds.stream().collect(Collectors.toMap(userId -> userId, userId -> roleId2RoleDO.get(userId2UserRoleRelDOs.get(userId).getRoleId())));
}
}

View File

@@ -1,57 +1,24 @@
package com.yinlihupo.enlish.service.service.user;
import cn.dev33.satoken.stp.StpUtil;
import com.yinlihupo.enlish.service.constant.RoleConstants;
import com.yinlihupo.enlish.service.domain.dataobject.RoleDO;
import com.yinlihupo.enlish.service.domain.dataobject.UserDO;
import com.yinlihupo.enlish.service.domain.dataobject.UserRoleRelDO;
import com.yinlihupo.enlish.service.domain.mapper.RoleDOMapper;
import com.yinlihupo.enlish.service.domain.mapper.UserDOMapper;
import com.yinlihupo.enlish.service.domain.mapper.UserRoleRelDOMapper;
import com.yinlihupo.enlish.service.service.UserService;
import com.yinlihupo.framework.common.util.JsonUtils;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service
@Slf4j
public class UserServiceImpl implements UserService {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Resource
private RoleDOMapper roleDOMapper;
@Resource
private UserRoleRelDOMapper userRoleRelDOMapper;
@Resource
private UserDOMapper userDOMapper;
@Override
public void pushRolePermission2Redis() {
List<RoleDO> roleDOS = roleDOMapper.selectAll();
List<String> roleKeys = roleDOS.stream().map(RoleDO::getRoleKey).toList();
log.info("将角色同步到 redis 中, {}", roleKeys);
stringRedisTemplate.opsForValue().set(RoleConstants.ROLE, JsonUtils.toJsonString(roleKeys), 60 * 60 * 24);
Map<Long, RoleDO> roleId2RoleDO = roleDOS.stream().collect(Collectors.toMap(RoleDO::getId, roleDO -> roleDO));
List<UserRoleRelDO> userRoleRelDOS = userRoleRelDOMapper.selectAll();
Map<Long, List<UserRoleRelDO>> userId2UserRoleRelDOs = userRoleRelDOS.stream().collect(Collectors.groupingBy(UserRoleRelDO::getUserId));
userId2UserRoleRelDOs.forEach((userId, userRoleRelDOs) -> {
List<Long> roleIds = userRoleRelDOs.stream().map(UserRoleRelDO::getRoleId).toList();
List<RoleDO> roleDOs = roleIds.stream().map(roleId2RoleDO::get).toList();
List<String> user2RoleKeys = roleDOs.stream().map(RoleDO::getRoleKey).toList();
log.info("将用户 {} 的角色同步到 redis 中, {}", userId, roleKeys);
stringRedisTemplate.opsForValue().set(RoleConstants.buildUserRoleKey(userId), JsonUtils.toJsonString(user2RoleKeys), 60 * 60 * 24);
});
}
@Override
public UserDO findUser() {
@@ -60,4 +27,20 @@ public class UserServiceImpl implements UserService {
return userDOMapper.selectById(loginId);
}
@Override
public List<UserDO> findUsersList(int page, int limit, String name) {
return userDOMapper.selectUserDOList(name, (page - 1) * limit, limit);
}
@Override
public Integer findUserTotal() {
return userDOMapper.selectUserTotal();
}
@Override
public void createUser(UserDO userDO) {
userDOMapper.insert(userDO);
}
}

View File

@@ -1,7 +1,7 @@
package com.yinlihupo.enlish.service.task;
import com.yinlihupo.enlish.service.service.UserService;
import com.yinlihupo.enlish.service.service.RoleService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
@@ -13,11 +13,11 @@ import org.springframework.stereotype.Component;
public class UserRoleTask {
@Resource
private UserService userService;
private RoleService roleService;
@Scheduled(cron = "0 0 1 * * ?")
public void PushRolePermissions2Redis() {
log.info("定时任务,将系统权限推送到 redis 中");
userService.pushRolePermission2Redis();
roleService.pushRolePermission2Redis();
}
}

View File

@@ -14,4 +14,11 @@
select * from role
</select>
<select id="selectByIds" resultMap="BaseResultMap">
select * from role where id in
<foreach item="item" collection="ids" separator="," open="(" close=")">
#{item}
</foreach>
</select>
</mapper>

View File

@@ -8,8 +8,6 @@
<result column="openid" jdbcType="VARCHAR" property="openid" />
<result column="phone" jdbcType="VARCHAR" property="phone" />
<result column="email" jdbcType="VARCHAR" property="email" />
<result column="status" jdbcType="INTEGER" property="status" />
<result column="is_deleted" jdbcType="INTEGER" property="isDeleted" />
</resultMap>
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
@@ -17,18 +15,36 @@
values (#{password}, #{phone}, #{name})
</insert>
<insert id="createUser">
insert into user (phone, name, password)
values (#{phone}, #{name}, #{password})
</insert>
<select id="selectByPhone" resultMap="BaseResultMap">
select *
from user
where phone = #{phone}
and is_deleted = 0
</select>
<select id="selectById">
select *
from user
where id = #{id}
and is_deleted = 0
</select>
<select id="selectUserDOList" resultMap="BaseResultMap">
select *
from user
where 1 = 1
<if test="name != null">
and name = #{name}
</if>
limit #{startIndex}, #{pageSize}
</select>
<select id="selectUserTotal" resultType="java.lang.Integer">
select count(*)
from user
</select>
</mapper>

View File

@@ -6,7 +6,7 @@
<result column="user_id" jdbcType="BIGINT" property="userId" />
<result column="role_id" jdbcType="BIGINT" property="roleId" />
<result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
<result column="is_deleted" jdbcType="BIT" property="isDeleted" />
</resultMap>
<select id="selectAll" resultMap="BaseResultMap">
select *
@@ -14,4 +14,13 @@
where is_deleted = 0
</select>
<select id="selectByUserIds" resultMap="BaseResultMap">
select *
from user_role_rel
where user_id in
<foreach item="item" index="index" collection="userIds" open="(" separator="," close=")">
#{item}
</foreach>
</select>
</mapper>