feat: 添加删除用户账户的接口,包含权限检查和自我删除限制

This commit is contained in:
zyh
2025-08-30 23:21:41 +08:00
parent 0c1dd71dca
commit 1ca34bde76
2 changed files with 53 additions and 0 deletions

View File

@@ -76,6 +76,31 @@ public class AccountController {
return accountService.resetPassword(id, req.getNewPassword(), Boolean.TRUE.equals(req.getForceLogout()));
}
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
@Operation(summary = "删除用户", description = "删除指定的用户账户,管理员不能删除自己或最后一个管理员")
public Mono<Void> delete(
@Parameter(description = "账户ID") @PathVariable Long id,
@Parameter(hidden = true) @RequestHeader("Authorization") String authHeader) {
return Mono.fromCallable(() -> {
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
throw new IllegalArgumentException("Authorization header is required");
}
String token = authHeader.substring(7);
io.jsonwebtoken.Claims claims = jwtService.parse(token);
Long currentUserId = claims.get("userId", Long.class);
if (currentUserId == null) {
throw new IllegalArgumentException("Invalid token: userId not found");
}
return currentUserId;
})
.flatMap(currentUserId -> accountService.delete(id, currentUserId))
.then();
}
@GetMapping("/me/points-balance")
@Operation(summary = "获取当前用户积分余额", description = "根据token解析用户ID并获取当前用户的积分余额")
public Mono<PointsBalanceResponse> getCurrentUserPointsBalance(

View File

@@ -163,6 +163,34 @@ public class AccountService {
.subscribeOn(Schedulers.boundedElastic());
}
@Transactional
public Mono<Boolean> delete(Long id, Long currentUserId) {
return Mono.fromCallable(() -> {
// 检查用户是否存在
UserAccount user = mapper.selectById(id);
if (user == null) {
throw new IllegalArgumentException("用户不存在");
}
// 不能删除自己
if (id.equals(currentUserId)) {
throw new IllegalArgumentException("不能删除当前登录的用户");
}
// 如果要删除的是管理员,检查是否是最后一个管理员
if ("ADMIN".equals(user.getUserType())) {
long adminCount = mapper.countByFilter("ADMIN", "ENABLED", null);
if (adminCount <= 1) {
throw new IllegalArgumentException("不能删除最后一个管理员账户");
}
}
// 执行删除
return mapper.deleteById(id) > 0;
})
.subscribeOn(Schedulers.boundedElastic());
}
private AccountResponse toResp(UserAccount a) {
if (a == null) return null;
AccountResponse r = new AccountResponse();