From 2581a77f0976ab3929a89fd44cb884cfc88d572b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=BF=A0=E6=9E=97?= <170083662@qq.com> Date: Mon, 3 Aug 2026 17:54:16 +0800 Subject: [PATCH] =?UTF-8?q?feat(auth):=20=E6=B7=BB=E5=8A=A0=E5=85=8D?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E5=88=87=E6=8D=A2=E7=A7=9F=E6=88=B7=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增接口获取当前用户可切换的租户列表,支持手机号和邮箱聚合租户 - 实现免登录切换租户接口,需判断当前用户权限及目标租户状态 - 编写辅助方法查找可切换的用户账户 - 添加将用户信息转换为租户选项的工具方法 - 在切换成功后生成新的访问令牌并保存登录记录 --- .../system/controller/MainController.java | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/src/main/java/com/gxwebsoft/common/system/controller/MainController.java b/src/main/java/com/gxwebsoft/common/system/controller/MainController.java index 9391456..7818e48 100644 --- a/src/main/java/com/gxwebsoft/common/system/controller/MainController.java +++ b/src/main/java/com/gxwebsoft/common/system/controller/MainController.java @@ -65,6 +65,7 @@ import java.net.URLEncoder; import java.text.MessageFormat; import java.time.Instant; import java.time.LocalDateTime; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.stream.Collectors; @@ -1131,6 +1132,105 @@ public class MainController extends BaseController { return success("登录成功", new LoginResult(access_token, user)); } + @Operation(summary = "获取当前登录用户可切换的租户列表(免登录切换前置)") + @GetMapping("/auth/switchableTenants") + public ApiResult switchableTenants() { + User current = getLoginUser(); + if (current == null) { + return fail("未登录或登录已过期", null); + } + List options = new ArrayList<>(); + // 按手机号聚合(仅管理员账号) + if (StrUtil.isNotBlank(current.getPhone())) { + LoginParam phoneParam = new LoginParam(); + phoneParam.setPhone(current.getPhone()); + List byPhone = userService.getAdminsByPhone(phoneParam); + if (byPhone != null) { + for (User u : byPhone) { + options.add(toTenantOption(u)); + } + } + } + // 按邮箱聚合(管理员账号,按租户去重) + if (StrUtil.isNotBlank(current.getEmail())) { + List byEmail = userService.findAccountsByEmail(current.getEmail()); + if (byEmail != null) { + for (User u : byEmail) { + if (!Boolean.TRUE.equals(u.getIsAdmin())) { + continue; + } + final Integer tId = u.getTenantId(); + if (tId != null && options.stream().noneMatch(o -> tId.equals(o.getTenantId()))) { + options.add(toTenantOption(u)); + } + } + } + } + return success(options); + } + + @Operation(summary = "免登录切换租户(需已登录,当前用户须为目标租户管理员)") + @PostMapping("/auth/switchTenant") + public ApiResult switchTenant(@RequestBody LoginParam param, HttpServletRequest request) { + Integer targetTenantId = param.getTenantId(); + if (targetTenantId == null) { + return fail("请选择租户", null); + } + User current = getLoginUser(); + if (current == null) { + return fail("未登录或登录已过期", null); + } + // 校验目标租户在可切换范围内(与 switchableTenants 一致) + User target = findSwitchableUser(current, targetTenantId); + if (target == null) { + return fail("无权切换到该租户", null); + } + if (!target.getStatus().equals(0)) { + return fail("该租户账号已被冻结", null); + } + String access_token = generateToken(target); + loginRecordService.saveAsync(target.getUsername(), LoginRecord.TYPE_LOGIN, null, target.getTenantId(), request); + return success("切换成功", new LoginResult(access_token, target)); + } + + private User findSwitchableUser(User current, Integer targetTenantId) { + List candidates = new ArrayList<>(); + if (StrUtil.isNotBlank(current.getPhone())) { + LoginParam phoneParam = new LoginParam(); + phoneParam.setPhone(current.getPhone()); + List byPhone = userService.getAdminsByPhone(phoneParam); + if (byPhone != null) { + candidates.addAll(byPhone); + } + } + if (StrUtil.isNotBlank(current.getEmail())) { + List byEmail = userService.findAccountsByEmail(current.getEmail()); + if (byEmail != null) { + for (User u : byEmail) { + if (Boolean.TRUE.equals(u.getIsAdmin())) { + candidates.add(u); + } + } + } + } + return candidates.stream() + .filter(u -> targetTenantId.equals(u.getTenantId())) + .findFirst() + .orElse(null); + } + + private TenantOption toTenantOption(User u) { + return new TenantOption( + u.getTenantId(), + u.getTenantName(), + u.getUserId(), + u.getUsername(), + u.getNickname(), + null, + u.getAvatar() + ); + } + /** * 生成 JWT token 并存入 Redis */