From 932d5b0824fa49657d697637a85a981a129b126a Mon Sep 17 00:00:00 2001 From: yinhuaiwei Date: Tue, 27 Oct 2026 17:25:52 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E4=B8=AD=E6=81=92tok?= =?UTF-8?q?en=E7=99=BB=E5=BD=95=E5=90=8E=E7=9A=84=E2=80=9C=E5=8A=A8?= =?UTF-8?q?=E7=8E=AF=E2=80=9D=E7=9B=B8=E5=85=B3=E8=A7=92=E8=89=B2=E5=90=8C?= =?UTF-8?q?=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/feign/RemoteExtUserService.java | 5 + .../factory/RemoteExtUserFallbackFactory.java | 5 + .../admin/service/JoinbrightLoginService.java | 19 ++++ .../inspect/system/client/ExtAuthClient.java | 5 + .../system/controller/ExtUserController.java | 18 ++++ .../inspect/system/domain/ext/ExtRole.java | 18 ++++ .../system/domain/ext/RoleSyncResult.java | 24 +++++ .../system/mapper/ExtOrgSyncMapper.java | 8 ++ .../system/service/IExtOrgSyncService.java | 6 ++ .../service/impl/ExtOrgSyncServiceImpl.java | 92 +++++++++++++++++++ .../mapper/ext_sync/ExtOrgSyncMapper.xml | 28 ++++++ 11 files changed, 228 insertions(+) create mode 100644 inspect-management/src/main/java/com/inspect/system/domain/ext/ExtRole.java create mode 100644 inspect-management/src/main/java/com/inspect/system/domain/ext/RoleSyncResult.java diff --git a/inspect-admin/src/main/java/com/inspect/admin/feign/RemoteExtUserService.java b/inspect-admin/src/main/java/com/inspect/admin/feign/RemoteExtUserService.java index 2355e8e..54fcdcc 100644 --- a/inspect-admin/src/main/java/com/inspect/admin/feign/RemoteExtUserService.java +++ b/inspect-admin/src/main/java/com/inspect/admin/feign/RemoteExtUserService.java @@ -8,6 +8,7 @@ import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestParam; /** * 调用 dliip-system 的内部接口,为外部登录用户自动建号 / 绑定本地账号。 @@ -22,4 +23,8 @@ public interface RemoteExtUserService { @PostMapping("/inner/ext_user/ensure") AjaxResult ensureUser(@RequestBody JoinbrightUser user, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); + + @PostMapping("/inner/ext_user/syncRoles") + AjaxResult syncRoles(@RequestParam("username") String username, + @RequestHeader(SecurityConstants.FROM_SOURCE) String source); } \ No newline at end of file diff --git a/inspect-admin/src/main/java/com/inspect/admin/feign/factory/RemoteExtUserFallbackFactory.java b/inspect-admin/src/main/java/com/inspect/admin/feign/factory/RemoteExtUserFallbackFactory.java index 58574a3..aa0def4 100644 --- a/inspect-admin/src/main/java/com/inspect/admin/feign/factory/RemoteExtUserFallbackFactory.java +++ b/inspect-admin/src/main/java/com/inspect/admin/feign/factory/RemoteExtUserFallbackFactory.java @@ -21,6 +21,11 @@ public class RemoteExtUserFallbackFactory implements FallbackFactory> getUserByDeptId(@RequestParam("deptId") String deptId, @RequestHeader("from-source") String source); + + @GetMapping("/inner/ext_auth/getRoleByUser") + Response> getRoleByUser(@RequestParam("username") String username, + @RequestHeader("from-source") String source); } \ No newline at end of file diff --git a/inspect-management/src/main/java/com/inspect/system/controller/ExtUserController.java b/inspect-management/src/main/java/com/inspect/system/controller/ExtUserController.java index 49ac145..c60fbae 100644 --- a/inspect-management/src/main/java/com/inspect/system/controller/ExtUserController.java +++ b/inspect-management/src/main/java/com/inspect/system/controller/ExtUserController.java @@ -5,11 +5,13 @@ import com.inspect.base.core.exception.ServiceException; import com.inspect.base.core.web.domain.AjaxResult; import com.inspect.common.security.annotation.InnerAuth; import com.inspect.system.domain.ext.ExtUser; +import com.inspect.system.domain.ext.RoleSyncResult; import com.inspect.system.service.IExtOrgSyncService; import lombok.extern.slf4j.Slf4j; 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.RequestParam; import org.springframework.web.bind.annotation.RestController; /** @@ -27,6 +29,22 @@ public class ExtUserController { this.extOrgSyncService = extOrgSyncService; } + @InnerAuth + @PostMapping("/syncRoles") + public AjaxResult syncRoles(@RequestParam("username") String username) { + try { + RoleSyncResult result = extOrgSyncService.syncUserRoles(username); + return AjaxResult.success("角色同步完成", result); + } catch (ServiceException e) { + log.warn("[EXT-LOGIN] 外部角色同步失败: {}", e.getMessage()); + return AjaxResult.error(e.getCode() == null ? ExtAuthConstants.SERVICE_UNAVAILABLE : e.getCode(), + e.getMessage()); + } catch (Exception e) { + log.error("[EXT-LOGIN] 外部角色同步异常", e); + return AjaxResult.error(ExtAuthConstants.SERVICE_UNAVAILABLE, "外部角色同步失败,请稍后重试"); + } + } + @InnerAuth @PostMapping("/ensure") public AjaxResult ensure(@RequestBody ExtUser extUser) { diff --git a/inspect-management/src/main/java/com/inspect/system/domain/ext/ExtRole.java b/inspect-management/src/main/java/com/inspect/system/domain/ext/ExtRole.java new file mode 100644 index 0000000..85d97a7 --- /dev/null +++ b/inspect-management/src/main/java/com/inspect/system/domain/ext/ExtRole.java @@ -0,0 +1,18 @@ +package com.inspect.system.domain.ext; + +import lombok.Data; + +/** + * 外部盐源平台用户角色(GET /ext_auth/getRoleByUser)。 + */ +@Data +public class ExtRole { + /** 用户角色关联 id */ + private String id; + /** 角色 id */ + private String roleId; + /** 角色名称,外部可能返回 null */ + private String roleName; + /** 用户 id */ + private String userId; +} diff --git a/inspect-management/src/main/java/com/inspect/system/domain/ext/RoleSyncResult.java b/inspect-management/src/main/java/com/inspect/system/domain/ext/RoleSyncResult.java new file mode 100644 index 0000000..f6ed2cb --- /dev/null +++ b/inspect-management/src/main/java/com/inspect/system/domain/ext/RoleSyncResult.java @@ -0,0 +1,24 @@ +package com.inspect.system.domain.ext; + +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * 登录时外部角色同步结果。 + */ +@Data +public class RoleSyncResult { + private String username; + /** 本次新增的本地角色名 */ + private List added = new ArrayList<>(); + /** 本次回收的本地角色名 */ + private List removed = new ArrayList<>(); + /** 跳过明细(未映射、本地不存在、已停用等) */ + private List skipped = new ArrayList<>(); + + public String getSummary() { + return String.format("角色(新增%d/回收%d/跳过%d)", added.size(), removed.size(), skipped.size()); + } +} diff --git a/inspect-management/src/main/java/com/inspect/system/mapper/ExtOrgSyncMapper.java b/inspect-management/src/main/java/com/inspect/system/mapper/ExtOrgSyncMapper.java index 3270b3b..1f43668 100644 --- a/inspect-management/src/main/java/com/inspect/system/mapper/ExtOrgSyncMapper.java +++ b/inspect-management/src/main/java/com/inspect/system/mapper/ExtOrgSyncMapper.java @@ -1,6 +1,7 @@ package com.inspect.system.mapper; import com.inspect.system.base.domain.SysDept; +import com.inspect.system.base.domain.SysRole; import com.inspect.system.base.domain.SysUser; import com.inspect.system.domain.ext.ExtDeptRef; import com.inspect.system.domain.ext.ExtUserRef; @@ -46,6 +47,13 @@ public interface ExtOrgSyncMapper { int insertUserRole(@Param("userId") Long userId, @Param("roleId") Long roleId); + /* ---------------- 角色(登录时同步) ---------------- */ + SysRole selectRoleByName(@Param("roleName") String roleName); + + List selectRolesByUserId(@Param("userId") Long userId); + + int deleteUserRole(@Param("userId") Long userId, @Param("roleId") Long roleId); + List selectSyncedUsers(); int deactivateUser(@Param("userId") Long userId); diff --git a/inspect-management/src/main/java/com/inspect/system/service/IExtOrgSyncService.java b/inspect-management/src/main/java/com/inspect/system/service/IExtOrgSyncService.java index f37c359..8319a50 100644 --- a/inspect-management/src/main/java/com/inspect/system/service/IExtOrgSyncService.java +++ b/inspect-management/src/main/java/com/inspect/system/service/IExtOrgSyncService.java @@ -2,6 +2,7 @@ package com.inspect.system.service; import com.inspect.system.domain.ExtSyncResult; import com.inspect.system.domain.ext.ExtUser; +import com.inspect.system.domain.ext.RoleSyncResult; /** * 外部平台(盐源智慧感知平台)部门 / 用户同步。 @@ -24,4 +25,9 @@ public interface IExtOrgSyncService { * @return 本地用户ID */ Long ensureUser(ExtUser extUser); + + /** + * 登录时同步外部角色:白名单映射 + 映射范围内对账回收,common 角色不参与回收。 + */ + RoleSyncResult syncUserRoles(String username); } \ No newline at end of file diff --git a/inspect-management/src/main/java/com/inspect/system/service/impl/ExtOrgSyncServiceImpl.java b/inspect-management/src/main/java/com/inspect/system/service/impl/ExtOrgSyncServiceImpl.java index 0d46bdc..7fb4ca0 100644 --- a/inspect-management/src/main/java/com/inspect/system/service/impl/ExtOrgSyncServiceImpl.java +++ b/inspect-management/src/main/java/com/inspect/system/service/impl/ExtOrgSyncServiceImpl.java @@ -9,13 +9,16 @@ import com.inspect.base.core.utils.StringUtils; import com.inspect.base.redis.service.RedisService; import com.inspect.common.security.utils.SecurityUtils; import com.inspect.system.base.domain.SysDept; +import com.inspect.system.base.domain.SysRole; import com.inspect.system.base.domain.SysUser; import com.inspect.system.client.ExtAuthClient; import com.inspect.system.domain.ExtSyncResult; import com.inspect.system.domain.ext.ExtDept; import com.inspect.system.domain.ext.ExtDeptRef; +import com.inspect.system.domain.ext.ExtRole; import com.inspect.system.domain.ext.ExtUser; import com.inspect.system.domain.ext.ExtUserRef; +import com.inspect.system.domain.ext.RoleSyncResult; import com.inspect.system.mapper.ExtOrgSyncMapper; import com.inspect.system.service.IExtOrgSyncService; import lombok.extern.slf4j.Slf4j; @@ -58,6 +61,11 @@ public class ExtOrgSyncServiceImpl implements IExtOrgSyncService { /** 同步用户默认角色:普通角色,可由 yanyuan.sync.default-role-key 覆盖(部分库为 common1) */ @Value("${yanyuan.sync.default-role-key:common}") private String defaultRoleKey; + + /** 仅同步以该前缀开头的外部角色名 */ + @Value("${yanyuan.sync.role-name-prefix:动环}") + private String roleNamePrefix; + private static final String LOCK_KEY = "ext:org:sync:lock"; private static final String ENSURE_LOCK_PREFIX = "ext:user:ensure:"; private static final long ENSURE_LOCK_SECONDS = 30L; @@ -309,6 +317,90 @@ public class ExtOrgSyncServiceImpl implements IExtOrgSyncService { } } + /** + * 登录时同步外部角色:只处理以配置前缀(默认动环)开头的外部角色名, + * 与本地 role_name 精确匹配后授权;回收范围同样限定为动环前缀角色。 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public RoleSyncResult syncUserRoles(String username) { + if (StringUtils.isEmpty(username)) { + throw new ServiceException("用户名不能为空", ExtAuthConstants.TOKEN_INVALID); + } + RoleSyncResult result = new RoleSyncResult(); + result.setUsername(username); + + SysUser user = extOrgSyncMapper.selectUserByUserName(username); + if (user == null || !"0".equals(user.getDelFlag())) { + throw new ServiceException("本地用户[" + username + "]不存在或已删除", + ExtAuthConstants.TOKEN_INVALID); + } + + if (StringUtils.isEmpty(roleNamePrefix)) { + log.info("[EXT-ROLE] 未配置角色名前缀,跳过角色同步, username={}", username); + return result; + } + + Response> response = extAuthClient.getRoleByUser(username, SOURCE); + if (response == null || response.getCode() != Response.SUCCESS || response.getData() == null) { + throw new ServiceException("获取外部角色失败", ExtAuthConstants.SERVICE_UNAVAILABLE); + } + + Map desired = new LinkedHashMap<>(); + for (ExtRole extRole : response.getData()) { + String roleName = StringUtils.trim(extRole.getRoleName()); + if (StringUtils.isEmpty(roleName)) { + result.getSkipped().add("外部角色名为空"); + continue; + } + if (!roleName.startsWith(roleNamePrefix)) { + continue; + } + SysRole localRole = extOrgSyncMapper.selectRoleByName(roleName); + if (localRole == null) { + result.getSkipped().add(roleName + "(本地角色不存在)"); + continue; + } + if (!"0".equals(localRole.getStatus())) { + result.getSkipped().add(roleName + "(本地角色已停用)"); + continue; + } + desired.put(localRole.getRoleId(), roleName); + } + + Long commonRoleId = extOrgSyncMapper.selectRoleIdByRoleKey(defaultRoleKey); + Set currentIds = new HashSet<>(); + for (SysRole current : extOrgSyncMapper.selectRolesByUserId(user.getUserId())) { + currentIds.add(current.getRoleId()); + if (commonRoleId != null && commonRoleId.equals(current.getRoleId())) { + continue; + } + String currentName = current.getRoleName(); + // 只回收动环前缀角色;非动环角色视为管理员手工授权,不参与对账 + if (StringUtils.isEmpty(currentName) || !currentName.startsWith(roleNamePrefix)) { + continue; + } + if (!desired.containsKey(current.getRoleId())) { + extOrgSyncMapper.deleteUserRole(user.getUserId(), current.getRoleId()); + result.getRemoved().add(currentName); + } + } + + for (Map.Entry entry : desired.entrySet()) { + if (currentIds.contains(entry.getKey())) { + continue; + } + extOrgSyncMapper.insertUserRole(user.getUserId(), entry.getKey()); + result.getAdded().add(entry.getValue()); + } + + log.info("[EXT-ROLE] username={}, {}", username, result.getSummary()); + for (String skipped : result.getSkipped()) { + log.warn("[EXT-ROLE] username={}, 跳过: {}", username, skipped); + } + return result; + } + /** * 用户落库:按 ext_id 更新;无 ext_id 记录时优先绑定同名账号(覆盖 ext_id),否则新建。 */ diff --git a/inspect-management/src/main/resources/mapper/ext_sync/ExtOrgSyncMapper.xml b/inspect-management/src/main/resources/mapper/ext_sync/ExtOrgSyncMapper.xml index 525b19f..67eaa2c 100644 --- a/inspect-management/src/main/resources/mapper/ext_sync/ExtOrgSyncMapper.xml +++ b/inspect-management/src/main/resources/mapper/ext_sync/ExtOrgSyncMapper.xml @@ -44,6 +44,14 @@ + + + + + + + + @@ -168,4 +176,24 @@ where user_id = #{userId} and status = '0' + + + + + + + delete from sys_user_role where user_id = #{userId} and role_id = #{roleId} + + \ No newline at end of file