|
|
@@ -6,9 +6,12 @@ import com.xuekairui.common.BusinessException;
|
|
|
import com.xuekairui.common.ErrorCode;
|
|
|
import com.xuekairui.user.dto.AdminUserResponse;
|
|
|
import com.xuekairui.user.dto.UpdateRoleRequest;
|
|
|
+import com.xuekairui.user.entity.Admin;
|
|
|
import com.xuekairui.user.entity.User;
|
|
|
-import com.xuekairui.user.enums.UserRole;
|
|
|
+import com.xuekairui.user.mapper.AdminMapper;
|
|
|
import com.xuekairui.user.mapper.UserMapper;
|
|
|
+import com.xuekairui.user.security.TokenVersionCache;
|
|
|
+import com.xuekairui.user.util.PasswordUtil;
|
|
|
import lombok.RequiredArgsConstructor;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
@@ -25,10 +28,12 @@ import java.util.Objects;
|
|
|
public class AdminUserService {
|
|
|
|
|
|
private final UserMapper userMapper;
|
|
|
+ private final AdminMapper adminMapper;
|
|
|
+ private final TokenVersionCache tokenVersionCache;
|
|
|
+ private final PasswordUtil passwordUtil;
|
|
|
|
|
|
/**
|
|
|
- * 分页查询用户列表(支持按ID精确/手机号模糊/昵称模糊/角色精确查询)
|
|
|
- * 无删除接口,用户数据不可删除。
|
|
|
+ * 分页查询前端用户列表(仅 USER 角色,不含管理员)
|
|
|
*/
|
|
|
public Page<AdminUserResponse> listUsers(Integer pageNum, Integer pageSize,
|
|
|
Long userId, String phone, String nickname, String role) {
|
|
|
@@ -43,21 +48,43 @@ public class AdminUserService {
|
|
|
if (nickname != null && !nickname.isBlank()) {
|
|
|
wrapper.like(User::getNickname, nickname);
|
|
|
}
|
|
|
- if (role != null && !role.isBlank()) {
|
|
|
- wrapper.eq(User::getRole, UserRole.fromCode(role));
|
|
|
- }
|
|
|
+ // 前端用户列表仅查 USER,管理员走 listAdmins
|
|
|
wrapper.orderByDesc(User::getCreateTime);
|
|
|
|
|
|
Page<User> userPage = userMapper.selectPage(page, wrapper);
|
|
|
Page<AdminUserResponse> resultPage = new Page<>(userPage.getCurrent(), userPage.getSize(), userPage.getTotal());
|
|
|
resultPage.setRecords(userPage.getRecords().stream()
|
|
|
.map(this::toResponse)
|
|
|
+ .map(r -> { r.setRole("USER"); return r; })
|
|
|
+ .toList());
|
|
|
+ return resultPage;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 分页查询管理员列表(查 t_admin 表)
|
|
|
+ */
|
|
|
+ public Page<AdminUserResponse> listAdmins(Integer pageNum, Integer pageSize) {
|
|
|
+ Page<Admin> page = new Page<>(pageNum, pageSize);
|
|
|
+ LambdaQueryWrapper<Admin> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ wrapper.orderByDesc(Admin::getCreateTime);
|
|
|
+ Page<Admin> adminPage = adminMapper.selectPage(page, wrapper);
|
|
|
+ Page<AdminUserResponse> resultPage = new Page<>(adminPage.getCurrent(), adminPage.getSize(), adminPage.getTotal());
|
|
|
+ resultPage.setRecords(adminPage.getRecords().stream()
|
|
|
+ .map(a -> AdminUserResponse.builder()
|
|
|
+ .id(a.getId())
|
|
|
+ .phone(a.getPhone())
|
|
|
+ .nickname(a.getUsername())
|
|
|
+ .role(a.getRole())
|
|
|
+ .status(a.getStatus())
|
|
|
+ .createTime(a.getCreateTime())
|
|
|
+ .build())
|
|
|
.toList());
|
|
|
return resultPage;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 修改用户角色(仅超级管理员可调用)
|
|
|
+ * 升级为 ADMIN/SUPER_ADMIN → 插入 t_admin;降级为 USER → 删除 t_admin 记录
|
|
|
*/
|
|
|
@Transactional
|
|
|
public AdminUserResponse updateUserRole(Long userId, UpdateRoleRequest request, Long operatorId) {
|
|
|
@@ -66,30 +93,60 @@ public class AdminUserService {
|
|
|
throw new BusinessException(ErrorCode.USER_NOT_FOUND);
|
|
|
}
|
|
|
|
|
|
- UserRole targetRole = UserRole.fromCode(request.getRole());
|
|
|
- UserRole currentRole = user.getRole() != null ? user.getRole() : UserRole.USER;
|
|
|
+ String targetRole = request.getRole();
|
|
|
+ Admin existingAdmin = adminMapper.selectById(userId);
|
|
|
|
|
|
// 禁止取消最后一个超级管理员
|
|
|
- if (currentRole == UserRole.SUPER_ADMIN && targetRole != UserRole.SUPER_ADMIN) {
|
|
|
- long otherSuperAdminCount = userMapper.selectCount(
|
|
|
- new LambdaQueryWrapper<User>()
|
|
|
- .eq(User::getRole, UserRole.SUPER_ADMIN)
|
|
|
- .ne(User::getId, userId));
|
|
|
- if (otherSuperAdminCount == 0) {
|
|
|
+ if (existingAdmin != null && "SUPER_ADMIN".equals(existingAdmin.getRole())
|
|
|
+ && !"SUPER_ADMIN".equals(targetRole)) {
|
|
|
+ long otherCount = adminMapper.selectCount(
|
|
|
+ new LambdaQueryWrapper<Admin>()
|
|
|
+ .eq(Admin::getRole, "SUPER_ADMIN")
|
|
|
+ .ne(Admin::getId, userId));
|
|
|
+ if (otherCount == 0) {
|
|
|
throw new BusinessException(ErrorCode.BUSINESS_ERROR, "系统中至少保留一个超级管理员");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 防止操作人取消自己的超级管理员权限后系统无超级管理员
|
|
|
if (Objects.equals(operatorId, userId)
|
|
|
- && currentRole == UserRole.SUPER_ADMIN
|
|
|
- && targetRole != UserRole.SUPER_ADMIN) {
|
|
|
+ && existingAdmin != null && "SUPER_ADMIN".equals(existingAdmin.getRole())
|
|
|
+ && !"SUPER_ADMIN".equals(targetRole)) {
|
|
|
throw new BusinessException(ErrorCode.BUSINESS_ERROR, "不能取消自己的超级管理员权限");
|
|
|
}
|
|
|
|
|
|
- user.setRole(targetRole);
|
|
|
- userMapper.updateById(user);
|
|
|
- log.info("操作人{}修改用户{}角色为{}", operatorId, userId, targetRole.getCode());
|
|
|
+ if ("ADMIN".equals(targetRole) || "SUPER_ADMIN".equals(targetRole)) {
|
|
|
+ // 升级:插入或更新 t_admin
|
|
|
+ if (existingAdmin != null) {
|
|
|
+ existingAdmin.setRole(targetRole);
|
|
|
+ adminMapper.updateById(existingAdmin);
|
|
|
+ } else {
|
|
|
+ // 如果没有密码,用手机号后6位作为初始密码(BCrypt加密)
|
|
|
+ String pwd = user.getPassword();
|
|
|
+ if (pwd == null || pwd.isEmpty()) {
|
|
|
+ String phone = user.getPhone();
|
|
|
+ String rawPwd = phone != null && phone.length() >= 6
|
|
|
+ ? phone.substring(phone.length() - 6) : "123456";
|
|
|
+ pwd = passwordUtil.encode(rawPwd);
|
|
|
+ }
|
|
|
+ Admin admin = Admin.builder()
|
|
|
+ .id(userId)
|
|
|
+ .username(user.getPhone() != null ? user.getPhone() : "admin_" + userId)
|
|
|
+ .password(pwd)
|
|
|
+ .phone(user.getPhone())
|
|
|
+ .role(targetRole)
|
|
|
+ .status(1)
|
|
|
+ .build();
|
|
|
+ adminMapper.insert(admin);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 降级为 USER:删除 t_admin 记录
|
|
|
+ if (existingAdmin != null) {
|
|
|
+ adminMapper.deleteById(userId);
|
|
|
+ tokenVersionCache.clear();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("操作人{}修改用户{}角色为{}", operatorId, userId, targetRole);
|
|
|
return toResponse(user);
|
|
|
}
|
|
|
|
|
|
@@ -103,19 +160,36 @@ public class AdminUserService {
|
|
|
}
|
|
|
long newVersion = (user.getTokenVersion() != null ? user.getTokenVersion() : 0L) + 1;
|
|
|
user.setTokenVersion(newVersion);
|
|
|
- // 同时清空 Refresh Token,防止旧 Refresh Token 还能刷新
|
|
|
user.setRefreshTokenHash(null);
|
|
|
user.setRefreshTokenStatus(null);
|
|
|
userMapper.updateById(user);
|
|
|
+ tokenVersionCache.putVersion(userId, false, newVersion);
|
|
|
log.info("管理员{}强制踢出用户{},新tokenVersion={}", operatorId, userId, newVersion);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 强制踢出管理员
|
|
|
+ */
|
|
|
+ public void kickAdmin(Long adminId, Long operatorId) {
|
|
|
+ Admin admin = adminMapper.selectById(adminId);
|
|
|
+ if (admin == null) {
|
|
|
+ throw new BusinessException(ErrorCode.USER_NOT_FOUND);
|
|
|
+ }
|
|
|
+ long newVersion = (admin.getTokenVersion() != null ? admin.getTokenVersion() : 0L) + 1;
|
|
|
+ admin.setTokenVersion(newVersion);
|
|
|
+ admin.setRefreshTokenHash(null);
|
|
|
+ admin.setRefreshTokenStatus(null);
|
|
|
+ adminMapper.updateById(admin);
|
|
|
+ tokenVersionCache.putVersion(adminId, true, newVersion);
|
|
|
+ log.info("管理员{}强制踢出管理员{},新tokenVersion={}", operatorId, adminId, newVersion);
|
|
|
+ }
|
|
|
+
|
|
|
private AdminUserResponse toResponse(User user) {
|
|
|
return AdminUserResponse.builder()
|
|
|
.id(user.getId())
|
|
|
.phone(user.getPhone())
|
|
|
.nickname(user.getNickname())
|
|
|
- .role(user.getRole() != null ? user.getRole().getCode() : UserRole.USER.getCode())
|
|
|
+ .role("USER")
|
|
|
.status(user.getStatus())
|
|
|
.createTime(user.getCreateTime())
|
|
|
.build();
|