diff --git a/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/tenant/PigxTenantConfiguration.java b/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/tenant/PigxTenantConfiguration.java index 959ae79c8..1e1b15768 100644 --- a/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/tenant/PigxTenantConfiguration.java +++ b/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/tenant/PigxTenantConfiguration.java @@ -23,19 +23,29 @@ import org.springframework.context.annotation.Configuration; import org.springframework.http.client.ClientHttpRequestInterceptor; /** + * 租户信息拦截配置类 + * * @author lengleng - * @date 2020/4/29 - *

- * 租户信息拦截 + * @date 2025/06/27 */ @Configuration public class PigxTenantConfiguration { + /** + * 创建并返回PigxFeignTenantInterceptor实例 + * + * @return RequestInterceptor实例 + */ @Bean public RequestInterceptor pigxFeignTenantInterceptor() { return new PigxFeignTenantInterceptor(); } + /** + * 创建并返回一个租户请求拦截器实例 + * + * @return 配置好的租户请求拦截器 + */ @Bean public ClientHttpRequestInterceptor pigxTenantRequestInterceptor() { return new TenantRequestInterceptor(); diff --git a/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/tenant/TenantBroker.java b/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/tenant/TenantBroker.java index 443a6e5be..381519658 100644 --- a/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/tenant/TenantBroker.java +++ b/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/tenant/TenantBroker.java @@ -43,105 +43,148 @@ import java.util.function.Supplier; @UtilityClass public class TenantBroker { - @FunctionalInterface - public interface RunAs { + /** + * 以指定租户身份执行操作 + * + * @param tenant 租户ID + * @param func 需要执行的操作 + * @throws TenantBrokerExceptionWrapper 执行过程中发生异常时抛出 + */ + public void runAs(Long tenant, RunAs func) { + final Long pre = TenantContextHolder.getTenantId(); + try { + log.trace("TenantBroker 切换租户{} -> {}", pre, tenant); + TenantContextHolder.setTenantId(tenant); + func.run(tenant); + } catch (Exception e) { + throw new TenantBrokerExceptionWrapper(e.getMessage(), e); + } finally { + log.trace("TenantBroker 还原租户{} <- {}", pre, tenant); + TenantContextHolder.setTenantId(pre); + } + } - /** - * 执行业务逻辑 - * @param tenantId - * @throws Exception - */ - void run(T tenantId) throws Exception; + /** + * 以指定租户身份执行操作 + * + * @param tenant 租户ID + * @param func 需要执行的操作函数 + * @param 操作返回的数据类型 + * @return 操作函数的执行结果 + * @throws TenantBrokerExceptionWrapper 执行过程中发生异常时抛出 + */ + public T applyAs(Long tenant, ApplyAs func) { + final Long pre = TenantContextHolder.getTenantId(); + try { + log.trace("TenantBroker 切换租户{} -> {}", pre, tenant); + TenantContextHolder.setTenantId(tenant); + return func.apply(tenant); + } catch (Exception e) { + throw new TenantBrokerExceptionWrapper(e.getMessage(), e); + } finally { + log.trace("TenantBroker 还原租户{} <- {}", pre, tenant); + TenantContextHolder.setTenantId(pre); + } + } - } + /** + * 以指定租户的身份执行操作 + * + * @param supplier 租户ID提供者 + * @param func 要执行的操作 + */ + public void runAs(Supplier supplier, RunAs func) { + runAs(supplier.get(), func); + } - @FunctionalInterface - public interface ApplyAs { + /** + * 执行给定的NoneAs操作,临时跳过租户上下文 + * + * @param noneAs 要执行的操作 + * @param 返回值的类型 + * @return 操作执行结果 + * @throws TenantBrokerExceptionWrapper 执行过程中\发生异常时抛出 + */ + public T noneAs(NoneAs noneAs) { + try { + TenantContextHolder.setTenantSkip(); + return noneAs.run(); + } catch (Exception e) { + throw new TenantBrokerExceptionWrapper(e.getMessage(), e); + } finally { + TenantContextHolder.unsetTenantSkip(); + } + } - /** - * 执行业务逻辑,返回一个值 - * @param tenantId - * @return - * @throws Exception - */ - R apply(T tenantId) throws Exception; + /** + * 以指定租户的身份执行操作 + * + * @param supplier 租户ID提供者 + * @param func 要执行的操作函数 + * @param 返回数据的类型 + * @return 操作函数的执行结果 + */ + public T applyAs(Supplier supplier, ApplyAs func) { + return applyAs(supplier.get(), func); + } - } + @FunctionalInterface + public interface RunAs { - /** - * 以某个租户的身份运行 - * @param tenant 租户ID - * @param func - */ - public void runAs(Long tenant, RunAs func) { - final Long pre = TenantContextHolder.getTenantId(); - try { - log.trace("TenantBroker 切换租户{} -> {}", pre, tenant); - TenantContextHolder.setTenantId(tenant); - func.run(tenant); - } - catch (Exception e) { - throw new TenantBrokerExceptionWrapper(e.getMessage(), e); - } - finally { - log.trace("TenantBroker 还原租户{} <- {}", pre, tenant); - TenantContextHolder.setTenantId(pre); - } - } + /** + * 执行业务逻辑 + * + * @param tenantId 租户ID + * @throws Exception 执行过程中可能抛出的异常 + */ + void run(T tenantId) throws Exception; - /** - * 以某个租户的身份运行 - * @param tenant 租户ID - * @param func - * @param 返回数据类型 - * @return - */ - public T applyAs(Long tenant, ApplyAs func) { - final Long pre = TenantContextHolder.getTenantId(); - try { - log.trace("TenantBroker 切换租户{} -> {}", pre, tenant); - TenantContextHolder.setTenantId(tenant); - return func.apply(tenant); - } - catch (Exception e) { - throw new TenantBrokerExceptionWrapper(e.getMessage(), e); - } - finally { - log.trace("TenantBroker 还原租户{} <- {}", pre, tenant); - TenantContextHolder.setTenantId(pre); - } - } + } - /** - * 以某个租户的身份运行 - * @param supplier - * @param func - */ - public void runAs(Supplier supplier, RunAs func) { - runAs(supplier.get(), func); - } - /** - * 以某个租户的身份运行 - * @param supplier - * @param func - * @param 返回数据类型 - * @return - */ - public T applyAs(Supplier supplier, ApplyAs func) { - return applyAs(supplier.get(), func); - } + @FunctionalInterface + public interface ApplyAs { - public static class TenantBrokerExceptionWrapper extends RuntimeException { + /** + * 执行业务逻辑,返回一个值 + * + * @param tenantId + * @return + * @throws Exception + */ + R apply(T tenantId) throws Exception; - public TenantBrokerExceptionWrapper(String message, Throwable cause) { - super(message, cause); - } + } - public TenantBrokerExceptionWrapper(Throwable cause) { - super(cause); - } + /** + * 无参数函数式接口:用于定义无参数但有返回值的业务逻辑执行方法 + * + * @author lengleng + * @date 2025/06/27 + */ + @FunctionalInterface + public interface NoneAs { - } + /** + * 执行业务逻辑 + * + * @return 业务执行结果 + * @throws Exception 执行过程中发生异常时抛出 + */ + R run() throws Exception; + + } + + public static class TenantBrokerExceptionWrapper extends RuntimeException { + + public TenantBrokerExceptionWrapper(String message, Throwable cause) { + super(message, cause); + } + + public TenantBrokerExceptionWrapper(Throwable cause) { + super(cause); + } + + } } diff --git a/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/tenant/TenantContextHolder.java b/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/tenant/TenantContextHolder.java index 79b2527af..f24110f2f 100644 --- a/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/tenant/TenantContextHolder.java +++ b/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/tenant/TenantContextHolder.java @@ -48,6 +48,13 @@ public class TenantContextHolder { THREAD_LOCAL_TENANT_SKIP_FLAG.set(Boolean.TRUE); } + /** + * 清除当前线程的租户跳过标记 + */ + public void unsetTenantSkip() { + THREAD_LOCAL_TENANT_SKIP_FLAG.remove(); + } + /** * 获取TTL中的租户ID * @return diff --git a/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/tenant/TenantRequestInterceptor.java b/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/tenant/TenantRequestInterceptor.java index 6832f082f..62a8de81b 100644 --- a/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/tenant/TenantRequestInterceptor.java +++ b/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/tenant/TenantRequestInterceptor.java @@ -9,13 +9,22 @@ import org.springframework.http.client.ClientHttpResponse; import java.io.IOException; /** + * RestTemplate 请求拦截器,用于传递租户ID + * * @author lengleng - * @date 2020/4/29 - *

- * 传递 RestTemplate 请求的租户ID + * @date 2025/06/27 */ public class TenantRequestInterceptor implements ClientHttpRequestInterceptor { + /** + * 拦截HTTP请求,添加租户ID到请求头 + * + * @param request HTTP请求对象 + * @param body 请求体字节数组 + * @param execution 请求执行器 + * @return 客户端HTTP响应 + * @throws IOException 当I/O操作失败时抛出 + */ @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { diff --git a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxRedisOAuth2AuthorizationService.java b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxRedisOAuth2AuthorizationService.java index b3b74c1cf..2d26c0205 100644 --- a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxRedisOAuth2AuthorizationService.java +++ b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxRedisOAuth2AuthorizationService.java @@ -2,11 +2,11 @@ package com.pig4cloud.pigx.common.security.service; import cn.hutool.core.collection.CollUtil; import com.pig4cloud.pigx.common.core.constant.SecurityConstants; -import com.pig4cloud.pigx.common.core.util.KeyStrResolver; import lombok.RequiredArgsConstructor; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.lang.Nullable; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.core.OAuth2AccessToken; import org.springframework.security.oauth2.core.OAuth2RefreshToken; @@ -17,6 +17,7 @@ import org.springframework.security.oauth2.server.authorization.OAuth2Authorizat import org.springframework.security.oauth2.server.authorization.OAuth2TokenType; import org.springframework.util.Assert; +import java.security.Principal; import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.List; @@ -37,8 +38,6 @@ public class PigxRedisOAuth2AuthorizationService implements OAuth2AuthorizationS private final RedisTemplate redisTemplate; - private final KeyStrResolver tenantKeyStrResolver; - private static boolean isState(OAuth2Authorization authorization) { return Objects.nonNull(authorization.getAttribute("state")); } @@ -97,10 +96,14 @@ public class PigxRedisOAuth2AuthorizationService implements OAuth2AuthorizationS .set(buildKey(OAuth2ParameterNames.ACCESS_TOKEN, accessToken.getTokenValue()), authorization, between, TimeUnit.SECONDS); - // 扩展记录 access-token 、username 的关系 1::token::username::admin::xxx - String tokenUsername = String.format("%s::%s::%s::%s::%s", tenantKeyStrResolver.key(), AUTHORIZATION, - SecurityConstants.DETAILS_USERNAME, authorization.getPrincipalName(), accessToken.getTokenValue()); - redisTemplate.opsForValue().set(tokenUsername, accessToken.getTokenValue(), between, TimeUnit.SECONDS); + UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = authorization.getAttribute(Principal.class.getName()); + + if (Objects.nonNull(usernamePasswordAuthenticationToken) && usernamePasswordAuthenticationToken.getPrincipal() instanceof PigxUser pigxUser) { + // 扩展记录 access-token 、username 的关系 token::username::admin::tenantId::xxx + String tokenUsername = String.format("%s::%s::%s::%s::%s::%s", AUTHORIZATION, + SecurityConstants.DETAILS_USERNAME, authorization.getPrincipalName(), authorization.getRegisteredClientId(), pigxUser.getTenantId(), accessToken.getTokenValue()); + redisTemplate.opsForValue().set(tokenUsername, accessToken.getTokenValue(), between, TimeUnit.SECONDS); + } } } @@ -131,8 +134,8 @@ public class PigxRedisOAuth2AuthorizationService implements OAuth2AuthorizationS keys.add(buildKey(OAuth2ParameterNames.ACCESS_TOKEN, accessToken.getTokenValue())); // 扩展记录 access-token 、username 的关系 1::token::username::admin::xxx - String key = String.format("%s::%s::%s::%s::%s", tenantKeyStrResolver.key(), AUTHORIZATION, - SecurityConstants.DETAILS_USERNAME, authorization.getPrincipalName(), accessToken.getTokenValue()); + String key = String.format("%s::%s::%s::%s::*::%s", AUTHORIZATION, + SecurityConstants.DETAILS_USERNAME, authorization.getPrincipalName(), authorization.getRegisteredClientId(), accessToken.getTokenValue()); keys.add(key); } @@ -155,21 +158,20 @@ public class PigxRedisOAuth2AuthorizationService implements OAuth2AuthorizationS } private String buildKey(String type, String id) { - return String.format("%s::%s::%s::%s", tenantKeyStrResolver.key(), AUTHORIZATION, type, id); + return String.format("%s::%s::%s", AUTHORIZATION, type, id); } /** - * 扩展方法根据 username 查询是否存在存储的 + * 根据用户名移除相关授权信息 * - * @param authentication - * @return + * @param authentication 认证信息,包含用户名 */ public void removeByUsername(Authentication authentication) { // 根据 username查询对应access-token String authenticationName = authentication.getName(); // 扩展记录 access-token 、username 的关系 1::token::username::admin::xxx - String tokenUsernameKey = String.format("%s::%s::%s::%s::*", tenantKeyStrResolver.key(), AUTHORIZATION, + String tokenUsernameKey = String.format("%s::%s::%s::*", AUTHORIZATION, SecurityConstants.DETAILS_USERNAME, authenticationName); Set keys = redisTemplate.keys(tokenUsernameKey); if (CollUtil.isEmpty(keys)) { diff --git a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxUserDetailsService.java b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxUserDetailsService.java index 69f8eeeef..9f1e21951 100644 --- a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxUserDetailsService.java +++ b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxUserDetailsService.java @@ -98,7 +98,7 @@ public interface PigxUserDetailsService extends UserDetailsService, Ordered { SysUser user = info.getSysUser(); // 构造security用户 - return new PigxUser(user.getUserId(), user.getUsername(), user.getDeptId(), user.getPhone(), user.getAvatar(), + return new PigxUser(user.getUserId(), user.getUsername(), info.getDeptId(), user.getPhone(), user.getAvatar(), user.getNickname(), user.getName(), user.getEmail(), user.getTenantId(), SecurityConstants.BCRYPT + user.getPassword() , true, true, UserTypeEnum.TOB.getStatus() diff --git a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/dto/UserInfo.java b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/dto/UserInfo.java index 42409e493..cb00fd84d 100644 --- a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/dto/UserInfo.java +++ b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/dto/UserInfo.java @@ -33,22 +33,28 @@ import java.io.Serializable; @Schema(description = "用户信息") public class UserInfo implements Serializable { - /** - * 用户基本信息 - */ - @Schema(description = "用户基本信息") - private SysUser sysUser; + /** + * 用户基本信息 + */ + @Schema(description = "用户基本信息") + private SysUser sysUser; - /** - * 权限标识集合 - */ - @Schema(description = "权限标识集合") - private String[] permissions; + /** + * 权限标识集合 + */ + @Schema(description = "权限标识集合") + private String[] permissions; - /** - * 角色集合 - */ - @Schema(description = "角色标识集合") - private Long[] roles; + /** + * 角色集合 + */ + @Schema(description = "角色标识集合") + private Long[] roles; + + /** + * 部门ID + */ + @Schema(description = "用户所属部门id") + private Long deptId; } diff --git a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/entity/SysTenantUser.java b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/entity/SysTenantUser.java new file mode 100644 index 000000000..ef6559338 --- /dev/null +++ b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/entity/SysTenantUser.java @@ -0,0 +1,55 @@ +/* + * + * Copyright (c) 2018-2025, lengleng All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * Neither the name of the pig4cloud.com developer nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * Author: lengleng (wangiegie@gmail.com) + * + */ + +package com.pig4cloud.pigx.admin.api.entity; + +import com.baomidou.mybatisplus.extension.activerecord.Model; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 租户用户信息实体类 + * + * @author lengleng + * @date 2025/06/27 + */ +@Data +@Schema(description = "租户用户信息") +@EqualsAndHashCode(callSuper = true) +public class SysTenantUser extends Model { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + @Schema(description = "用户id") + private Long userId; + + /** + * 租户ID + */ + @Schema(description = "租户ID") + private Long tenantId; + +} diff --git a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/entity/SysUser.java b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/entity/SysUser.java index 4385c72f1..35df109eb 100644 --- a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/entity/SysUser.java +++ b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/entity/SysUser.java @@ -146,11 +146,6 @@ public class SysUser extends Model implements Serializable { @Schema(description = "头像地址") private String avatar; - /** - * 部门ID - */ - @Schema(description = "用户所属部门id") - private Long deptId; /** * 租户ID @@ -219,5 +214,4 @@ public class SysUser extends Model implements Serializable { @PropertyName("邮箱") @Schema(description = "邮箱") private String email; - } diff --git a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/entity/SysUserDept.java b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/entity/SysUserDept.java new file mode 100644 index 000000000..225df6d73 --- /dev/null +++ b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/entity/SysUserDept.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2020 pig4cloud Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.pig4cloud.pigx.admin.api.entity; + +import com.baomidou.mybatisplus.extension.activerecord.Model; +import com.pig4cloud.pigx.common.core.util.TenantTable; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 系统用户部门关联实体类 + * + * @author lengleng + * @date 2025/06/27 + */ +@Data +@TenantTable +@EqualsAndHashCode(callSuper = true) +public class SysUserDept extends Model { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + @Schema(description = "用户id") + private Long userId; + + /** + * 岗位ID + */ + @Schema(description = "岗位id") + private Long deptId; + +} diff --git a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/entity/SysUserPost.java b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/entity/SysUserPost.java index 0018a45ab..5f9614144 100644 --- a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/entity/SysUserPost.java +++ b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/entity/SysUserPost.java @@ -17,21 +17,25 @@ package com.pig4cloud.pigx.admin.api.entity; import com.baomidou.mybatisplus.extension.activerecord.Model; +import com.pig4cloud.pigx.common.core.util.TenantTable; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode; +import java.io.Serial; + /** - *

- * 用户岗位表 - *

+ * 用户岗位表实体类 * - * @author fxz + * @author lengleng + * @date 2025/06/27 */ @Data +@TenantTable @EqualsAndHashCode(callSuper = true) public class SysUserPost extends Model { + @Serial private static final long serialVersionUID = 1L; /** diff --git a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/entity/SysUserRole.java b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/entity/SysUserRole.java index de5de054d..3a91c6dd6 100644 --- a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/entity/SysUserRole.java +++ b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/entity/SysUserRole.java @@ -20,10 +20,13 @@ package com.pig4cloud.pigx.admin.api.entity; import com.baomidou.mybatisplus.extension.activerecord.Model; +import com.pig4cloud.pigx.common.core.util.TenantTable; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode; +import java.io.Serial; + /** *

* 用户角色表 @@ -33,22 +36,31 @@ import lombok.EqualsAndHashCode; * @since 2017-10-29 */ @Data +@TenantTable @Schema(description = "用户角色") @EqualsAndHashCode(callSuper = true) public class SysUserRole extends Model { - private static final long serialVersionUID = 1L; + @Serial + private static final long serialVersionUID = 1L; - /** - * 用户ID - */ - @Schema(description = "用户id") - private Long userId; + /** + * 用户ID + */ + @Schema(description = "用户id") + private Long userId; - /** - * 角色ID - */ - @Schema(description = "角色id") - private Long roleId; + /** + * 角色ID + */ + @Schema(description = "角色id") + private Long roleId; + + + /** + * 租户ID + */ + @Schema(description = "租户ID") + private Long tenantId; } diff --git a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/vo/UserExcelVO.java b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/vo/UserExcelVO.java index 4c5180fb1..ccfa6fe77 100644 --- a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/vo/UserExcelVO.java +++ b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/vo/UserExcelVO.java @@ -4,9 +4,9 @@ import com.alibaba.excel.annotation.ExcelIgnore; import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.write.style.ColumnWidth; import com.pig4cloud.pigx.common.excel.annotation.ExcelLine; +import jakarta.validation.constraints.NotBlank; import lombok.Data; -import jakarta.validation.constraints.NotBlank; import java.io.Serializable; import java.time.LocalDateTime; @@ -75,7 +75,7 @@ public class UserExcelVO implements Serializable { */ @NotBlank(message = "部门名称不能为空") @ExcelProperty("部门名称") - private String deptName; + private String deptNameList; /** * 角色列表 diff --git a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/vo/UserVO.java b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/vo/UserVO.java index e6f384e6d..91d887544 100644 --- a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/vo/UserVO.java +++ b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/vo/UserVO.java @@ -20,6 +20,7 @@ package com.pig4cloud.pigx.admin.api.vo; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.pig4cloud.pigx.admin.api.entity.SysDept; import com.pig4cloud.pigx.admin.api.entity.SysPost; import com.pig4cloud.pigx.admin.api.entity.SysRole; import com.pig4cloud.pigx.common.sensitive.annotation.Sensitive; @@ -27,6 +28,7 @@ import com.pig4cloud.pigx.common.sensitive.core.SensitiveTypeEnum; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import java.io.Serial; import java.io.Serializable; import java.time.LocalDateTime; import java.util.List; @@ -39,163 +41,158 @@ import java.util.List; @Schema(description = "前端用户展示对象") public class UserVO implements Serializable { - private static final long serialVersionUID = 1L; + @Serial + private static final long serialVersionUID = 1L; - /** - * 主键ID - */ - @Schema(description = "主键") - private Long userId; + /** + * 主键ID + */ + @Schema(description = "主键") + private Long userId; - /** - * 用户名 - */ - @Schema(description = "用户名") - private String username; + /** + * 用户名 + */ + @Schema(description = "用户名") + private String username; - /** - * 密码 - */ - @JsonIgnore - private String password; + /** + * 密码 + */ + @JsonIgnore + private String password; - /** - * 随机盐 - */ - @JsonIgnore - private String salt; + /** + * 随机盐 + */ + @JsonIgnore + private String salt; - /** - * 微信openid - */ - @Schema(description = "微信open id") - private String wxOpenid; + /** + * 微信openid + */ + @Schema(description = "微信open id") + private String wxOpenid; - /** - * QQ openid - */ - @Schema(description = "qq open id") - private String qqOpenid; + /** + * QQ openid + */ + @Schema(description = "qq open id") + private String qqOpenid; - /** - * gitee openid - */ - @Schema(description = "gitee open id") - private String giteeOpenId; + /** + * gitee openid + */ + @Schema(description = "gitee open id") + private String giteeOpenId; - /** - * 开源中国 openid - */ - @Schema(description = "开源中国 open id") - private String oscOpenId; + /** + * 开源中国 openid + */ + @Schema(description = "开源中国 open id") + private String oscOpenId; - /** - * 企微微信 userid - */ - @Schema(description = "企微微信 userid") - private String wxCpUserid; + /** + * 企微微信 userid + */ + @Schema(description = "企微微信 userid") + private String wxCpUserid; - /** - * 钉钉 userid - */ - @Schema(description = "钉钉 userid") - private String wxDingUserid; + /** + * 钉钉 userid + */ + @Schema(description = "钉钉 userid") + private String wxDingUserid; - /** - * 创建时间 - */ - @Schema(description = "创建时间") - private LocalDateTime createTime; + /** + * 创建时间 + */ + @Schema(description = "创建时间") + private LocalDateTime createTime; - /** - * 修改时间 - */ - @Schema(description = "修改时间") - private LocalDateTime updateTime; + /** + * 修改时间 + */ + @Schema(description = "修改时间") + private LocalDateTime updateTime; - /** - * 0-正常,1-删除 - */ - @Schema(description = "删除标记,1:已删除,0:正常") - private String delFlag; + /** + * 0-正常,1-删除 + */ + @Schema(description = "删除标记,1:已删除,0:正常") + private String delFlag; - /** - * 锁定标记 - */ - @Schema(description = "锁定标记,0:正常,9:已锁定") - private String lockFlag; + /** + * 锁定标记 + */ + @Schema(description = "锁定标记,0:正常,9:已锁定") + private String lockFlag; - /** - * 密码过期标记 - */ - @Schema(description = "密码过期标记") - private String passwordExpireFlag; + /** + * 密码过期标记 + */ + @Schema(description = "密码过期标记") + private String passwordExpireFlag; - /** - * 密码修改时间 - */ - @Schema(description = "密码修改时间") - private LocalDateTime passwordModifyTime; + /** + * 密码修改时间 + */ + @Schema(description = "密码修改时间") + private LocalDateTime passwordModifyTime; - /** - * 手机号 - */ - @Sensitive(type = SensitiveTypeEnum.MOBILE_PHONE) - @Schema(description = "手机号") - private String phone; + /** + * 手机号 + */ + @Sensitive(type = SensitiveTypeEnum.MOBILE_PHONE) + @Schema(description = "手机号") + private String phone; - /** - * 头像 - */ - @Schema(description = "头像") - private String avatar; + /** + * 头像 + */ + @Schema(description = "头像") + private String avatar; - /** - * 部门ID - */ - @Schema(description = "所属部门") - private Long deptId; - /** - * 租户ID - */ - @Schema(description = "所属租户") - private Long tenantId; + /** + * 租户ID + */ + @Schema(description = "所属租户") + private Long tenantId; - /** - * 部门名称 - */ - @Schema(description = "所属部门名称") - private String deptName; - /** - * 角色列表 - */ - @Schema(description = "拥有的角色列表") - private List roleList; + @Schema(description = "所属部门名称") + private List deptList; - /** - * 岗位列表 - */ - private List postList; + /** + * 角色列表 + */ + @Schema(description = "拥有的角色列表") + private List roleList; - /** - * 昵称 - */ - @Schema(description = "昵称") - private String nickname; + /** + * 岗位列表 + */ + @Schema(description = "拥有的岗位列表") + private List postList; - /** - * 姓名 - */ - @Schema(description = "姓名") - private String name; + /** + * 昵称 + */ + @Schema(description = "昵称") + private String nickname; - /** - * 邮箱 - */ - @Schema(description = "邮箱") - private String email; + /** + * 姓名 + */ + @Schema(description = "姓名") + private String name; + + /** + * 邮箱 + */ + @Schema(description = "邮箱") + private String email; } diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysTenantController.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysTenantController.java index 98ccb3dfc..6a593e4f6 100755 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysTenantController.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysTenantController.java @@ -59,118 +59,143 @@ import java.util.List; @SecurityRequirement(name = HttpHeaders.AUTHORIZATION) public class SysTenantController { - private final SysTenantService sysTenantService; + private final SysTenantService sysTenantService; - private final SysMenuService sysMenuService; + private final SysMenuService sysMenuService; - /** - * 分页查询 - * @param page 分页对象 - * @param sysTenant 租户 - * @return - */ - @GetMapping("/page") - public R getSysTenantPage(@ParameterObject Page page, @ParameterObject SysTenant sysTenant) { - return R.ok(sysTenantService.page(page, Wrappers.lambdaQuery() - .like(StrUtil.isNotBlank(sysTenant.getName()), SysTenant::getName, sysTenant.getName()))); - } + /** + * 分页查询 + * + * @param page 分页对象 + * @param sysTenant 租户 + * @return + */ + @GetMapping("/page") + public R getSysTenantPage(@ParameterObject Page page, @ParameterObject SysTenant sysTenant) { + return R.ok(sysTenantService.page(page, Wrappers.lambdaQuery() + .like(StrUtil.isNotBlank(sysTenant.getName()), SysTenant::getName, sysTenant.getName()))); + } - /** - * 通过ID 查询租户信息 - * @param id ID - * @return R - */ - @GetMapping("/details/{id}") - public R getById(@PathVariable Long id) { - return R.ok(sysTenantService.getById(id)); - } + /** + * 通过ID 查询租户信息 + * + * @param id ID + * @return R + */ + @GetMapping("/details/{id}") + public R getById(@PathVariable Long id) { + return R.ok(sysTenantService.getById(id)); + } - /** - * 查询租户信息 - * @param query 查询条件 - * @return 租户信息 - */ - @GetMapping("/details") - public R getDetails(@ParameterObject SysTenant query) { - return R.ok(sysTenantService.getOne(Wrappers.query(query), false)); - } + /** + * 查询租户信息 + * + * @param query 查询条件 + * @return 租户信息 + */ + @GetMapping("/details") + public R getDetails(@ParameterObject SysTenant query) { + return R.ok(sysTenantService.getOne(Wrappers.query(query), false)); + } - /** - * 新增租户 - * @param sysTenant 租户 - * @return R - */ - @SysLog("新增租户") - @PostMapping - @HasPermission("sys_systenant_add") - @CacheEvict(value = CacheConstants.TENANT_DETAILS, allEntries = true) - public R save(@RequestBody SysTenant sysTenant) { - return R.ok(sysTenantService.saveTenant(sysTenant)); - } + /** + * 新增租户 + * + * @param sysTenant 租户 + * @return R + */ + @SysLog("新增租户") + @PostMapping + @HasPermission("sys_systenant_add") + @CacheEvict(value = CacheConstants.TENANT_DETAILS, allEntries = true) + public R save(@RequestBody SysTenant sysTenant) { + return R.ok(sysTenantService.saveTenant(sysTenant)); + } - /** - * 修改租户 - * @param sysTenant 租户 - * @return R - */ - @SysLog("修改租户") - @PutMapping - @HasPermission("sys_systenant_edit") - public R updateById(@RequestBody SysTenant sysTenant) { - return R.ok(sysTenantService.updateTenant(sysTenant)); - } + /** + * 修改租户 + * + * @param sysTenant 租户 + * @return R + */ + @SysLog("修改租户") + @PutMapping + @HasPermission("sys_systenant_edit") + public R updateById(@RequestBody SysTenant sysTenant) { + return R.ok(sysTenantService.updateTenant(sysTenant)); + } - /** - * 通过id删除租户 - *

- * 为了保证安全,这里只删除租户表的数据,不删除基础表中的租户初始化数据。 - * @param ids id 列表 - * @return R - */ - @SysLog("删除租户") - @DeleteMapping - @HasPermission("sys_systenant_del") - @CacheEvict(value = CacheConstants.TENANT_DETAILS, allEntries = true) - public R removeById(@RequestBody Long[] ids) { - return R.ok(sysTenantService.removeBatchByIds(CollUtil.toList(ids))); - } + /** + * 通过id删除租户 + *

+ * 为了保证安全,这里只删除租户表的数据,不删除基础表中的租户初始化数据。 + * + * @param ids id 列表 + * @return R + */ + @SysLog("删除租户") + @DeleteMapping + @HasPermission("sys_systenant_del") + @CacheEvict(value = CacheConstants.TENANT_DETAILS, allEntries = true) + public R removeById(@RequestBody Long[] ids) { + return R.ok(sysTenantService.removeBatchByIds(CollUtil.toList(ids))); + } - /** - * 查询全部有效的租户 - * @return - */ - @Inner(value = false) - @GetMapping("/list") - public R list() { - List tenants = sysTenantService.getNormalTenant() - .stream() - .filter(tenant -> tenant.getStartTime().isBefore(LocalDateTime.now())) - .filter(tenant -> tenant.getEndTime().isAfter(LocalDateTime.now())) + /** + * 查询全部有效的租户列表 + * + * @return 包含有效租户列表的响应结果 + */ + @Inner(value = false) + @GetMapping("/list") + public R list() { + List tenants = sysTenantService.getNormalTenant() + .stream() + .filter(tenant -> tenant.getStartTime().isBefore(LocalDateTime.now())) + .filter(tenant -> tenant.getEndTime().isAfter(LocalDateTime.now())) .toList(); - return R.ok(tenants); - } + return R.ok(tenants); + } - /** - * 导出excel 表格 - * @return - */ - @ResponseExcel - @GetMapping("/export") - @HasPermission("sys_systenant_export") - public List export(SysTenant sysTenant, Long[] ids) { - return sysTenantService - .list(Wrappers.lambdaQuery(sysTenant).in(ArrayUtil.isNotEmpty(ids), SysTenant::getId, CollUtil.toList(ids))); - } + /** + * 导出租户信息到Excel + * + * @param sysTenant 租户查询条件 + * @param ids 租户ID数组 + * @return 符合条件的租户列表 + */ + @ResponseExcel + @GetMapping("/export") + @HasPermission("sys_systenant_export") + public List export(SysTenant sysTenant, Long[] ids) { + return sysTenantService + .list(Wrappers.lambdaQuery(sysTenant).in(ArrayUtil.isNotEmpty(ids), SysTenant::getId, CollUtil.toList(ids))); + } - @GetMapping(value = "/tree/menu") - public R getTree() { - Long defaultId = ParamResolver.getLong("TENANT_DEFAULT_ID", 1L); - List> trees = new ArrayList<>(); - TenantBroker.runAs(defaultId, (id) -> { - trees.addAll(sysMenuService.treeMenu(null, null, null)); - }); + /** + * 获取租户菜单 + * + * @return 包含菜单树结构的响应结果 + */ + @GetMapping(value = "/tree/menu") + public R getTree() { + Long defaultId = ParamResolver.getLong("TENANT_DEFAULT_ID", 1L); + List> trees = new ArrayList<>(); + TenantBroker.runAs(defaultId, (id) -> { + trees.addAll(sysMenuService.treeMenu(null, null, null)); + }); - return R.ok(trees); - } + return R.ok(trees); + } + + /** + * 获取用户租户信息 + * + * @return 包含用户租户信息的响应结果 + */ + @GetMapping("/user-tenant") + public R getUserTenant() { + return R.ok(sysTenantService.getUserTenant()); + } } diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysUserController.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysUserController.java index 46c2d194a..47fc193e7 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysUserController.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysUserController.java @@ -31,6 +31,7 @@ import com.pig4cloud.pigx.common.core.constant.CommonConstants; import com.pig4cloud.pigx.common.core.exception.ErrorCodes; import com.pig4cloud.pigx.common.core.util.MsgUtils; import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.common.data.tenant.TenantBroker; import com.pig4cloud.pigx.common.excel.annotation.RequestExcel; import com.pig4cloud.pigx.common.excel.annotation.ResponseExcel; import com.pig4cloud.pigx.common.log.annotation.SysLog; @@ -50,8 +51,10 @@ import org.springframework.web.bind.annotation.*; import java.util.List; /** + * 用户管理控制器:提供用户相关的REST接口 + * * @author lengleng - * @date 2018/12/16 + * @date 2025/07/04 */ @RestController @AllArgsConstructor @@ -70,11 +73,12 @@ public class SysUserController { @Inner @GetMapping("/info/{username}") public R info(@PathVariable String username) { - SysUser user = userService.getOne(Wrappers.query().lambda().eq(SysUser::getUsername, username)); + SysUser user = TenantBroker.noneAs(() -> userService.getOne(Wrappers.query().lambda().eq(SysUser::getUsername, username), false)); if (user == null) { return R.failed(MsgUtils.getMessage(ErrorCodes.SYS_USER_USERINFO_EMPTY, username)); } - return R.ok(userService.findUserInfo(user)); + + return TenantBroker.applyAs(user.getTenantId(), tenantId -> R.ok(userService.findUserInfo(user))); } /** @@ -85,15 +89,18 @@ public class SysUserController { @GetMapping(value = {"/info"}) public R info() { String username = SecurityUtils.getUser().getUsername(); - SysUser user = userService.getOne(Wrappers.query().lambda().eq(SysUser::getUsername, username)); + SysUser user = TenantBroker.noneAs(() -> userService.getOne(Wrappers.query().lambda().eq(SysUser::getUsername, username), false)); if (user == null) { return R.failed(MsgUtils.getMessage(ErrorCodes.SYS_USER_QUERY_ERROR)); } - // UserInfo 是复用对象,不能通过设置 @jsonIgnore 来忽略密码字段 - UserInfo userInfo = userService.findUserInfo(user); - userInfo.getSysUser().setPassword(null); - return R.ok(userInfo); + + return TenantBroker.applyAs(user.getTenantId(), tenantId -> { + // UserInfo 是复用对象,不能通过设置 @jsonIgnore 来忽略密码字段 + UserInfo userInfo = userService.findUserInfo(user); + userInfo.getSysUser().setPassword(null); + return R.ok(userInfo); + }); } /** @@ -191,12 +198,27 @@ public class SysUserController { * @return R 返回结果对象,包含修改密码操作的结果信息 */ @PutMapping("/personal/password") - public R updatePassword(@RequestBody UserDTO userDto) { + public R updateUserPassword(@RequestBody UserDTO userDto) { String username = SecurityUtils.getUser().getUsername(); userDto.setUsername(username); return userService.changePassword(userDto); } + /** + * 更新用户租户信息 + * + * @param userDto 用户数据传输对象,包含需要更新的用户信息 + * @return 操作结果 + */ + @PutMapping("/personal/tenant") + public R updateUserTenant(@RequestBody UserDTO userDto) { + String username = SecurityUtils.getUser().getUsername(); + userDto.setUsername(username); + userDto.setUserId(SecurityUtils.getUser().getId()); + return userService.updateUserTenant(userDto); + } + + /** * @param username 用户名称 * @return 上级部门用户列表 diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/mapper/SysDeptMapper.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/mapper/SysDeptMapper.java index e50fb1848..249b4a45e 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/mapper/SysDeptMapper.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/mapper/SysDeptMapper.java @@ -19,19 +19,31 @@ package com.pig4cloud.pigx.admin.mapper; +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; import com.pig4cloud.pigx.admin.api.entity.SysDept; import com.pig4cloud.pigx.common.data.datascope.PigxBaseMapper; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; /** - *

* 部门管理 Mapper 接口 - *

* * @author lengleng - * @since 2018-01-20 + * @date 2025/06/27 */ @Mapper public interface SysDeptMapper extends PigxBaseMapper { + /** + * 根据用户ID和租户ID查询部门列表 + * + * @param userId 用户ID + * @param tenantId 租户ID + * @return 部门列表 + */ + @InterceptorIgnore(tenantLine = "true") + List listDeptsByUserId(@Param("userId") Long userId, @Param("tenantId") Long tenantId); + } diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/mapper/SysPostMapper.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/mapper/SysPostMapper.java index bd289ea09..08552c1c8 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/mapper/SysPostMapper.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/mapper/SysPostMapper.java @@ -17,9 +17,11 @@ package com.pig4cloud.pigx.admin.mapper; +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; import com.pig4cloud.pigx.admin.api.entity.SysPost; import com.pig4cloud.pigx.common.data.datascope.PigxBaseMapper; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; import java.util.List; @@ -32,11 +34,14 @@ import java.util.List; @Mapper public interface SysPostMapper extends PigxBaseMapper { - /** - * 通过用户ID,查询岗位信息 - * @param userId 用户id - * @return 岗位信息 - */ - List listPostsByUserId(Long userId); + /** + * 通过用户ID和租户ID查询岗位信息 + * + * @param userId 用户ID + * @param tenantId 租户ID + * @return 岗位信息列表 + */ + @InterceptorIgnore(tenantLine = "true") + List listPostsByUserId(@Param("userId") Long userId, @Param("tenantId") Long tenantId); } diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/mapper/SysRoleMapper.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/mapper/SysRoleMapper.java index 7752f8b6e..97467348f 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/mapper/SysRoleMapper.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/mapper/SysRoleMapper.java @@ -19,9 +19,11 @@ package com.pig4cloud.pigx.admin.mapper; +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; import com.pig4cloud.pigx.admin.api.entity.SysRole; import com.pig4cloud.pigx.common.data.datascope.PigxBaseMapper; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; import java.util.List; @@ -36,11 +38,14 @@ import java.util.List; @Mapper public interface SysRoleMapper extends PigxBaseMapper { - /** - * 通过用户ID,查询角色信息 - * @param userId - * @return - */ - List listRolesByUserId(Long userId); + /** + * 通过用户ID查询角色信息 + * + * @param userId 用户ID + * @param tenantId 租户ID + * @return 角色信息列表 + */ + @InterceptorIgnore(tenantLine = "true") + List listRolesByUserId(@Param("userId") Long userId, @Param("tenantId") Long tenantId); } diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/mapper/SysTenantUserMapper.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/mapper/SysTenantUserMapper.java new file mode 100644 index 000000000..a72c1190c --- /dev/null +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/mapper/SysTenantUserMapper.java @@ -0,0 +1,36 @@ +/* + * + * Copyright (c) 2018-2025, lengleng All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * Neither the name of the pig4cloud.com developer nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * Author: lengleng (wangiegie@gmail.com) + * + */ + +package com.pig4cloud.pigx.admin.mapper; + +import com.pig4cloud.pigx.admin.api.entity.SysTenantUser; +import com.pig4cloud.pigx.common.data.datascope.PigxBaseMapper; +import org.apache.ibatis.annotations.Mapper; + + +/** + * 系统租户用户映射接口 + * + * @author lengleng + * @date 2025/06/27 + */ +@Mapper +public interface SysTenantUserMapper extends PigxBaseMapper { + +} diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/mapper/SysUserDeptMapper.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/mapper/SysUserDeptMapper.java new file mode 100644 index 000000000..ae60f3dab --- /dev/null +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/mapper/SysUserDeptMapper.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2020 pig4cloud Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.pig4cloud.pigx.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.pig4cloud.pigx.admin.api.entity.SysUserDept; +import org.apache.ibatis.annotations.Mapper; + + +/** + * 系统用户部门关联Mapper接口 + * + * @author lengleng + * @date 2025/06/27 + */ +@Mapper +public interface SysUserDeptMapper extends BaseMapper { + +} diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/SysTenantService.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/SysTenantService.java index f5aa7aae1..104ca8c85 100755 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/SysTenantService.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/SysTenantService.java @@ -31,23 +31,32 @@ import java.util.List; public interface SysTenantService extends IService { /** - * 获取正常的租户 - * @return + * 获取正常的租户列表 + * + * @return 正常的租户列表 */ List getNormalTenant(); /** - * 保存租户 - * @param sysTenant - * @return + * 保存租户信息 + * + * @param sysTenant 租户信息对象 + * @return 保存是否成功 */ Boolean saveTenant(SysTenant sysTenant); /** - * 修改租户 - * @param sysTenant - * @return + * 修改租户信息 + * + * @param sysTenant 租户信息对象 + * @return 修改是否成功 */ Boolean updateTenant(SysTenant sysTenant); + /** + * 获取用户所属租户列表 + * + * @return 用户所属租户列表 + */ + List getUserTenant(); } diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/SysUserService.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/SysUserService.java index 576c16819..4cbe743b1 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/SysUserService.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/SysUserService.java @@ -195,4 +195,12 @@ public interface SysUserService extends IService { * @return */ R forgetUserPassword(RegisterUserDTO userDto, String code); + + /** + * 更新用户租户信息 + * + * @param userDto 用户数据传输对象 + * @return 更新结果 + */ + R updateUserTenant(UserDTO userDto); } diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysDeptServiceImpl.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysDeptServiceImpl.java index 52945be30..aeca0ab8c 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysDeptServiceImpl.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysDeptServiceImpl.java @@ -27,6 +27,7 @@ import cn.hutool.core.lang.tree.TreeUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.github.yulichang.wrapper.MPJLambdaWrapper; import com.pig4cloud.pigx.admin.api.constant.OrgTypeEnum; import com.pig4cloud.pigx.admin.api.constant.PostCodeEnum; import com.pig4cloud.pigx.admin.api.entity.*; @@ -66,10 +67,13 @@ public class SysDeptServiceImpl extends ServiceImpl impl private final SysRoleMapper roleMapper; + private final SysUserDeptMapper userDeptMapper; + private final SysUserPostMapper userPostMapper; /** * 删除部门 + * * @param id 部门 ID * @return 成功、失败 */ @@ -86,6 +90,7 @@ public class SysDeptServiceImpl extends ServiceImpl impl /** * 查询全部部门树 + * * @param deptName * @param parentId * @return 树 部门名称 @@ -94,33 +99,33 @@ public class SysDeptServiceImpl extends ServiceImpl impl public List> selectTree(String deptName, Long parentId) { // 查询全部部门 List deptAllList = deptMapper - .selectList(Wrappers.lambdaQuery().like(StrUtil.isNotBlank(deptName), SysDept::getName, deptName)); + .selectList(Wrappers.lambdaQuery().like(StrUtil.isNotBlank(deptName), SysDept::getName, deptName)); // 查询数据权限内部门 List deptOwnIdList = deptMapper - .selectListByScope( - Wrappers.lambdaQuery().like(StrUtil.isNotBlank(deptName), SysDept::getName, deptName), - DataScope.of()) - .stream() - .map(SysDept::getDeptId) - .toList(); + .selectListByScope( + Wrappers.lambdaQuery().like(StrUtil.isNotBlank(deptName), SysDept::getName, deptName), + DataScope.of()) + .stream() + .map(SysDept::getDeptId) + .toList(); // 权限内部门 List> collect = deptAllList.stream() - .filter(dept -> dept.getDeptId().intValue() != dept.getParentId()) - .sorted(Comparator.comparingInt(SysDept::getSortOrder)) - .map(dept -> { - TreeNode treeNode = new TreeNode<>(); - treeNode.setId(dept.getDeptId()); - treeNode.setParentId(dept.getParentId()); - treeNode.setName(dept.getName()); - treeNode.setWeight(dept.getSortOrder()); - // 有权限不返回标识 - Map extra = new HashMap<>(8); - extra.put("isLock", !deptOwnIdList.contains(dept.getDeptId())); - extra.put("createTime", dept.getCreateTime()); - treeNode.setExtra(extra); - return treeNode; - }) + .filter(dept -> dept.getDeptId().intValue() != dept.getParentId()) + .sorted(Comparator.comparingInt(SysDept::getSortOrder)) + .map(dept -> { + TreeNode treeNode = new TreeNode<>(); + treeNode.setId(dept.getDeptId()); + treeNode.setParentId(dept.getParentId()); + treeNode.setName(dept.getName()); + treeNode.setWeight(dept.getSortOrder()); + // 有权限不返回标识 + Map extra = new HashMap<>(8); + extra.put("isLock", !deptOwnIdList.contains(dept.getDeptId())); + extra.put("createTime", dept.getCreateTime()); + treeNode.setExtra(extra); + return treeNode; + }) .toList(); // 模糊查询 不组装树结构 直接返回 表格方便编辑 @@ -138,6 +143,7 @@ public class SysDeptServiceImpl extends ServiceImpl impl /** * 导出部门 + * * @return */ @Override @@ -147,10 +153,10 @@ public class SysDeptServiceImpl extends ServiceImpl impl DeptExcelVO deptExcelVo = new DeptExcelVO(); deptExcelVo.setName(item.getName()); Optional first = this.list() - .stream() - .filter(it -> item.getParentId().equals(it.getDeptId())) - .map(SysDept::getName) - .findFirst(); + .stream() + .filter(it -> item.getParentId().equals(it.getDeptId())) + .map(SysDept::getName) + .findFirst(); deptExcelVo.setParentName(first.orElse("根部门")); deptExcelVo.setSortOrder(item.getSortOrder()); return deptExcelVo; @@ -183,8 +189,7 @@ public class SysDeptServiceImpl extends ServiceImpl impl sysDept.setParentId(one.getDeptId()); sysDept.setSortOrder(item.getSortOrder()); baseMapper.insert(sysDept); - } - else { + } else { // 数据不合法情况 errorMessageList.add(new ErrorMessage(item.getLineNum(), errorMsg)); } @@ -197,6 +202,7 @@ public class SysDeptServiceImpl extends ServiceImpl impl /** * 查询所有子节点 (包含当前节点) + * * @param deptId 部门ID 目标部门ID * @return ID */ @@ -216,37 +222,39 @@ public class SysDeptServiceImpl extends ServiceImpl impl /** * 获取部门负责人 - * + *

* 1. 根据dept 查询用户 2. 筛选用户列表中 post + * * @param deptId deptId * @return user id list */ @Override public List listDeptLeader(Long deptId) { - List sysUserList = userMapper - .selectList(Wrappers.lambdaQuery().eq(SysUser::getDeptId, deptId)); - if (CollUtil.isEmpty(sysUserList)) { + + List userDeptList = userDeptMapper.selectList(Wrappers.lambdaQuery().eq(SysUserDept::getDeptId, deptId)); + if (CollUtil.isEmpty(userDeptList)) { return null; } SysPost deptLeader = postMapper - .selectOne(Wrappers.lambdaQuery().eq(SysPost::getPostCode, PostCodeEnum.TEAM_LEADER.getCode())); + .selectOne(Wrappers.lambdaQuery().eq(SysPost::getPostCode, PostCodeEnum.TEAM_LEADER.getCode())); if (deptLeader == null) { return null; } - List userIdList = sysUserList.stream().map(SysUser::getUserId).toList(); + List userIdList = userDeptList.stream().map(SysUserDept::getUserId).toList(); return userPostMapper.selectList(Wrappers.lambdaQuery().in(SysUserPost::getUserId, userIdList)) - .stream() - .filter(post -> Objects.equals(post.getPostId(), deptLeader.getPostId())) - .map(SysUserPost::getUserId) - .toList(); + .stream() + .filter(post -> Objects.equals(post.getPostId(), deptLeader.getPostId())) + .map(SysUserPost::getUserId) + .toList(); } /** * 根据父部门ID和类型获取组织树 + * * @param parentDeptId 父部门ID - * @param type 类型 + * @param type 类型 * @return 组织树信息 */ public Map listOrgTree(Long parentDeptId, String type) { @@ -267,15 +275,15 @@ public class SysDeptServiceImpl extends ServiceImpl impl }); return Dict.create() - .set("roleList", orgs) - .set("childDepartments", orgs) - .set("employees", new ArrayList<>()); + .set("roleList", orgs) + .set("childDepartments", orgs) + .set("employees", new ArrayList<>()); } Dict dict = Dict.create() - .set("titleDepartments", new ArrayList<>()) - .set("roleList", new ArrayList<>()) - .set("employees", new ArrayList<>()); + .set("titleDepartments", new ArrayList<>()) + .set("roleList", new ArrayList<>()) + .set("employees", new ArrayList<>()); List deptList = this.list( Wrappers.lambdaQuery().eq(Objects.nonNull(parentDeptId), SysDept::getParentId, parentDeptId)); @@ -296,8 +304,12 @@ public class SysDeptServiceImpl extends ServiceImpl impl List userVoList = new ArrayList<>(); - List userList = userMapper - .selectList(Wrappers.lambdaQuery().in(SysUser::getDeptId, CollUtil.toList(parentDeptId))); + MPJLambdaWrapper userQuery = new MPJLambdaWrapper<>(); + userQuery.selectAll(SysUser.class) + .leftJoin(SysUserDept.class, SysUserDept::getUserId, SysUser::getUserId) + .eq(SysUserDept::getDeptId, parentDeptId); + + List userList = userMapper.selectJoinList(userQuery); userList.forEach(user -> { OrgTreeVO orgTreeVo = new OrgTreeVO(); @@ -322,28 +334,30 @@ public class SysDeptServiceImpl extends ServiceImpl impl /** * 模糊搜索用户 + * * @param username 用户名/拼音/首字母 * @return 匹配到的用户 */ public List getOrgTreeUser(String username) { return userMapper.selectList(Wrappers.lambdaQuery().like(SysUser::getUsername, username)) - .stream() - .map(user -> { - OrgTreeVO orgTreeVo = new OrgTreeVO(); - orgTreeVo.setId(user.getUserId()); - orgTreeVo.setName(user.getUsername()); - orgTreeVo.setType(OrgTypeEnum.USER.getType()); - orgTreeVo.setSelected(false); - orgTreeVo.setAvatar(user.getAvatar()); - return orgTreeVo; - }) + .stream() + .map(user -> { + OrgTreeVO orgTreeVo = new OrgTreeVO(); + orgTreeVo.setId(user.getUserId()); + orgTreeVo.setName(user.getUsername()); + orgTreeVo.setType(OrgTypeEnum.USER.getType()); + orgTreeVo.setSelected(false); + orgTreeVo.setAvatar(user.getAvatar()); + return orgTreeVo; + }) .toList(); } /** * 递归查询所有子节点。 + * * @param allDeptList 所有部门列表 - * @param parentId 父部门ID + * @param parentId 父部门ID * @param resDeptList 结果集合 */ private void recursiveDept(List allDeptList, Long parentId, List resDeptList) { diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysRoleServiceImpl.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysRoleServiceImpl.java index 1cba9dd36..a5e0b352a 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysRoleServiceImpl.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysRoleServiceImpl.java @@ -35,6 +35,7 @@ import com.pig4cloud.pigx.common.core.constant.CacheConstants; import com.pig4cloud.pigx.common.core.exception.ErrorCodes; import com.pig4cloud.pigx.common.core.util.MsgUtils; import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.common.data.tenant.TenantContextHolder; import com.pig4cloud.pigx.common.excel.vo.ErrorMessage; import lombok.AllArgsConstructor; import org.springframework.cache.annotation.Cacheable; @@ -61,13 +62,14 @@ public class SysRoleServiceImpl extends ServiceImpl impl private SysRoleMenuService roleMenuService; /** - * 通过用户ID,查询角色信息 - * @param userId - * @return + * 通过用户ID查询角色信息 + * + * @param userId 用户ID + * @return 角色信息列表 */ @Override public List findRolesByUserId(Long userId) { - return baseMapper.listRolesByUserId(userId); + return baseMapper.listRolesByUserId(userId, TenantContextHolder.getTenantId()); } /** diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysTenantServiceImpl.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysTenantServiceImpl.java index 163822f45..503b2a094 100755 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysTenantServiceImpl.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysTenantServiceImpl.java @@ -22,11 +22,13 @@ import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.github.yulichang.wrapper.MPJLambdaWrapper; import com.pig4cloud.pigx.admin.api.constant.TenantStateEnum; import com.pig4cloud.pigx.admin.api.entity.*; import com.pig4cloud.pigx.admin.config.ClientDetailsInitRunner; import com.pig4cloud.pigx.admin.mapper.SysRoleMenuMapper; import com.pig4cloud.pigx.admin.mapper.SysTenantMapper; +import com.pig4cloud.pigx.admin.mapper.SysTenantUserMapper; import com.pig4cloud.pigx.admin.service.*; import com.pig4cloud.pigx.common.core.constant.CacheConstants; import com.pig4cloud.pigx.common.core.constant.CommonConstants; @@ -34,6 +36,7 @@ import com.pig4cloud.pigx.common.core.util.SpringContextHolder; import com.pig4cloud.pigx.common.data.datascope.DataScopeTypeEnum; import com.pig4cloud.pigx.common.data.resolver.ParamResolver; import com.pig4cloud.pigx.common.data.tenant.TenantBroker; +import com.pig4cloud.pigx.common.security.util.SecurityUtils; import lombok.AllArgsConstructor; import lombok.Data; import lombok.SneakyThrows; @@ -84,6 +87,8 @@ public class SysTenantServiceImpl extends ServiceImpl getUserTenant() { + MPJLambdaWrapper wrapper = new MPJLambdaWrapper() + .selectAll(SysTenant.class) + .leftJoin(SysTenantUser.class, SysTenantUser::getTenantId, SysTenant::getId) + .eq(SysTenantUser::getUserId, SecurityUtils.getUser().getId()); + return baseMapper.selectJoinList(wrapper); + } + /** * 保存新的租户菜单,维护成新的菜单 * diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysUserServiceImpl.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysUserServiceImpl.java index 0ae8505ac..f9c6e0a77 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysUserServiceImpl.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysUserServiceImpl.java @@ -36,9 +36,7 @@ import com.pig4cloud.pigx.admin.api.dto.UserInfo; import com.pig4cloud.pigx.admin.api.entity.*; import com.pig4cloud.pigx.admin.api.vo.UserExcelVO; import com.pig4cloud.pigx.admin.api.vo.UserVO; -import com.pig4cloud.pigx.admin.mapper.SysUserMapper; -import com.pig4cloud.pigx.admin.mapper.SysUserPostMapper; -import com.pig4cloud.pigx.admin.mapper.SysUserRoleMapper; +import com.pig4cloud.pigx.admin.mapper.*; import com.pig4cloud.pigx.admin.service.*; import com.pig4cloud.pigx.common.audit.annotation.Audit; import com.pig4cloud.pigx.common.core.constant.CacheConstants; @@ -49,6 +47,7 @@ import com.pig4cloud.pigx.common.core.util.MsgUtils; import com.pig4cloud.pigx.common.core.util.R; import com.pig4cloud.pigx.common.data.datascope.DataScope; import com.pig4cloud.pigx.common.data.resolver.ParamResolver; +import com.pig4cloud.pigx.common.data.tenant.TenantBroker; import com.pig4cloud.pigx.common.excel.vo.ErrorMessage; import com.pig4cloud.pigx.common.security.service.PigxUser; import com.pig4cloud.pigx.common.security.util.SecurityUtils; @@ -92,7 +91,12 @@ public class SysUserServiceImpl extends ServiceImpl impl private final SysUserPostMapper sysUserPostMapper; + private final SysUserDeptMapper sysUserDeptMapper; + + private final SysTenantUserMapper sysTenantUserMapper; + private final CacheManager cacheManager; + private final SysDeptMapper sysDeptMapper; private final StringRedisTemplate redisTemplate; @@ -141,14 +145,30 @@ public class SysUserServiceImpl extends ServiceImpl impl userRole.setRoleId(roleId); return userRole; }).forEach(sysUserRoleMapper::insert); + + // 插入用户部门信息 + if (Objects.nonNull(userDto.getDeptId())) { + SysUserDept sysUserDept = new SysUserDept(); + sysUserDept.setUserId(sysUser.getUserId()); + sysUserDept.setDeptId(userDto.getDeptId()); + sysUserDeptMapper.insertOrUpdate(List.of(sysUserDept), (batchSqlSession, sysUserDept1) -> sysUserDeptMapper.exists(Wrappers.lambdaQuery() + .eq(SysUserDept::getDeptId, userDto.getDeptId()) + .eq(SysUserDept::getUserId, sysUserDept1.getUserId()))); + } + + // 插入用户租户关系表 + SysTenantUser sysTenantUser = new SysTenantUser(); + sysTenantUser.setUserId(sysUser.getUserId()); + sysTenantUser.setTenantId(SecurityUtils.getUser().getTenantId()); + sysTenantUserMapper.insert(sysTenantUser); return Boolean.TRUE; } /** - * 通过查用户的全部信息 + * 查询用户全部信息,包括角色和权限 * - * @param sysUser 用户 - * @return + * @param sysUser 用户信息 + * @return 包含用户角色和权限的用户信息对象 */ @Override public UserInfo findUserInfo(SysUser sysUser) { @@ -184,6 +204,7 @@ public class SysUserServiceImpl extends ServiceImpl impl */ @Override public IPage getUsersWithRolePage(Page page, UserDTO userDTO) { + userDTO.setTenantId(SecurityUtils.getUser().getTenantId()); return baseMapper.getUserVosPage(page, userDTO, DataScope.of()); } @@ -214,7 +235,12 @@ public class SysUserServiceImpl extends ServiceImpl impl cache.evict(sysUser.getUsername()); } + // 删除用户角色 sysUserRoleMapper.delete(Wrappers.lambdaQuery().in(SysUserRole::getUserId, CollUtil.toList(ids))); + sysUserDeptMapper.delete(Wrappers.lambdaQuery().in(SysUserDept::getUserId, CollUtil.toList(ids))); + sysUserPostMapper.delete(Wrappers.lambdaQuery().in(SysUserPost::getUserId, CollUtil.toList(ids))); + sysTenantUserMapper.delete(Wrappers.lambdaQuery().in(SysTenantUser::getUserId, CollUtil.toList(ids)) + .eq(SysTenantUser::getTenantId, SecurityUtils.getUser().getTenantId())); this.removeBatchByIds(CollUtil.toList(ids)); return Boolean.TRUE; } @@ -273,6 +299,15 @@ public class SysUserServiceImpl extends ServiceImpl impl }).forEach(SysUserPost::insert); } + // 更新用户部门表 + if (Objects.nonNull(userDto.getDeptId())) { + sysUserDeptMapper.delete(Wrappers.lambdaQuery().eq(SysUserDept::getUserId, userDto.getUserId())); + SysUserDept sysUserDept = new SysUserDept(); + sysUserDept.setUserId(sysUser.getUserId()); + sysUserDept.setDeptId(userDto.getDeptId()); + sysUserDept.insert(); + } + return Boolean.TRUE; } @@ -286,13 +321,17 @@ public class SysUserServiceImpl extends ServiceImpl impl public List listAncestorUsers(String username) { SysUser sysUser = this.getOne(Wrappers.query().lambda().eq(SysUser::getUsername, username)); - SysDept sysDept = sysDeptService.getById(sysUser.getDeptId()); - if (sysDept == null) { + // 查询所属部门 + List sysDeptList = sysDeptMapper.listDeptsByUserId(sysUser.getUserId(), sysUser.getTenantId()); + if (CollUtil.isEmpty(sysDeptList)) { return null; } - Long parentId = sysDept.getParentId(); - return this.list(Wrappers.query().lambda().eq(SysUser::getDeptId, parentId)); + // 查询用户部门ID + List parentDeptList = sysDeptList.stream().map(SysDept::getParentId).toList(); + List userIdList = sysUserDeptMapper.selectList(Wrappers.lambdaQuery().in(SysUserDept::getDeptId, parentDeptList)) + .stream().map(SysUserDept::getUserId).toList(); + return this.list(Wrappers.lambdaQuery().in(SysUser::getUserId, userIdList)); } /** @@ -305,21 +344,29 @@ public class SysUserServiceImpl extends ServiceImpl impl @Override public List listUser(UserDTO userDTO, Long[] ids) { // 根据数据权限查询全部的用户信息 + userDTO.setTenantId(SecurityUtils.getUser().getTenantId()); List voList = baseMapper.selectVoListByScope(userDTO, ids, DataScope.of()); // 转换成execl 对象输出 List userExcelVOList = voList.stream().map(userVO -> { UserExcelVO excelVO = new UserExcelVO(); BeanUtils.copyProperties(userVO, excelVO); + // 转换角色名称 String roleNameList = userVO.getRoleList() .stream() .map(SysRole::getRoleName) .collect(Collectors.joining(StrUtil.COMMA)); excelVO.setRoleNameList(roleNameList); + // 转化岗位名称 String postNameList = userVO.getPostList() .stream() .map(SysPost::getPostName) .collect(Collectors.joining(StrUtil.COMMA)); excelVO.setPostNameList(postNameList); + // 转化部门名称 + String deptNameList = userVO.getDeptList().stream() + .map(SysDept::getName) + .collect(Collectors.joining(StrUtil.COMMA)); + excelVO.setDeptNameList(deptNameList); return excelVO; }).toList(); return userExcelVOList; @@ -356,10 +403,10 @@ public class SysUserServiceImpl extends ServiceImpl impl // 判断输入的部门名称列表是否合法 Optional deptOptional = deptList.stream() - .filter(dept -> excel.getDeptName().equals(dept.getName())) + .filter(dept -> excel.getDeptNameList().equals(dept.getName())) .findFirst(); if (!deptOptional.isPresent()) { - errorMsg.add(MsgUtils.getMessage(ErrorCodes.SYS_DEPT_DEPTNAME_INEXISTENCE, excel.getDeptName())); + errorMsg.add(MsgUtils.getMessage(ErrorCodes.SYS_DEPT_DEPTNAME_INEXISTENCE, excel.getDeptNameList())); } // 判断输入的角色名称列表是否合法 @@ -423,6 +470,11 @@ public class SysUserServiceImpl extends ServiceImpl impl userDTO.setRole(roleIdList); // 插入用户 this.saveUser(userDTO); + // 插入用户租户关系表 + SysTenantUser sysTenantUser = new SysTenantUser(); + sysTenantUser.setUserId(userDTO.getUserId()); + sysTenantUser.setTenantId(SecurityUtils.getUser().getTenantId()); + sysTenantUserMapper.insert(sysTenantUser); } /** @@ -499,6 +551,21 @@ public class SysUserServiceImpl extends ServiceImpl impl return R.ok(); } + + /** + * 更新用户租户信息 + * + * @param userDto 用户数据传输对象 + * @return 更新结果 + */ + @Override + @CacheEvict(value = CacheConstants.USER_DETAILS, key = "#userDto.username") + public R updateUserTenant(UserDTO userDto) { + return R.ok(TenantBroker.noneAs(() -> this.update(Wrappers.lambdaUpdate() + .set(SysUser::getTenantId, userDto.getTenantId()) + .eq(SysUser::getUserId, userDto.getUserId())))); + } + @Override public R unbinding(String type) { PigxUser user = SecurityUtils.getUser(); @@ -588,7 +655,8 @@ public class SysUserServiceImpl extends ServiceImpl impl return R.failed("验证码错误"); } - String username = lambdaQuery().select(SysUser::getUsername).eq(SysUser::getPhone, userDto.getPhone()).one().getUsername(); + String username = lambdaQuery().select(SysUser::getUsername). + eq(SysUser::getPhone, userDto.getPhone()).one().getUsername(); // 重置密码 String password = ENCODER.encode(userDto.getNewpassword1()); @@ -617,7 +685,9 @@ public class SysUserServiceImpl extends ServiceImpl impl */ @Override public List listUserIdByDeptIds(List deptIdList) { - return baseMapper.selectList(Wrappers.lambdaQuery().in(SysUser::getDeptId, deptIdList)); + List userList = sysUserDeptMapper.selectList(Wrappers.lambdaQuery().in(SysUserDept::getDeptId, deptIdList)) + .stream().map(SysUserDept::getUserId).toList(); + return baseMapper.selectList(Wrappers.lambdaQuery().in(SysUser::getUserId, userList)); } } diff --git a/pigx-upms/pigx-upms-biz/src/main/resources/mapper/SysDeptMapper.xml b/pigx-upms/pigx-upms-biz/src/main/resources/mapper/SysDeptMapper.xml index 81b39533f..0ea564cea 100644 --- a/pigx-upms/pigx-upms-biz/src/main/resources/mapper/SysDeptMapper.xml +++ b/pigx-upms/pigx-upms-biz/src/main/resources/mapper/SysDeptMapper.xml @@ -20,4 +20,15 @@ + diff --git a/pigx-upms/pigx-upms-biz/src/main/resources/mapper/SysPostMapper.xml b/pigx-upms/pigx-upms-biz/src/main/resources/mapper/SysPostMapper.xml index 8d523e8d5..f5f3b1603 100644 --- a/pigx-upms/pigx-upms-biz/src/main/resources/mapper/SysPostMapper.xml +++ b/pigx-upms/pigx-upms-biz/src/main/resources/mapper/SysPostMapper.xml @@ -48,6 +48,7 @@ WHERE p.post_id = up.post_id AND p.del_flag = '0' and up.user_id = #{userId} + and up.tenant_id = #{tenantId} diff --git a/pigx-upms/pigx-upms-biz/src/main/resources/mapper/SysRoleMapper.xml b/pigx-upms/pigx-upms-biz/src/main/resources/mapper/SysRoleMapper.xml index e7eaa6ea1..bffc9d670 100644 --- a/pigx-upms/pigx-upms-biz/src/main/resources/mapper/SysRoleMapper.xml +++ b/pigx-upms/pigx-upms-biz/src/main/resources/mapper/SysRoleMapper.xml @@ -49,5 +49,6 @@ WHERE sys_role.role_id = sys_user_role.role_id AND sys_role.del_flag = '0' and sys_user_role.user_id = #{userId} + and sys_user_role.tenant_id = #{tenantId} diff --git a/pigx-upms/pigx-upms-biz/src/main/resources/mapper/SysUserMapper.xml b/pigx-upms/pigx-upms-biz/src/main/resources/mapper/SysUserMapper.xml index 7deae29c1..843688beb 100644 --- a/pigx-upms/pigx-upms-biz/src/main/resources/mapper/SysUserMapper.xml +++ b/pigx-upms/pigx-upms-biz/src/main/resources/mapper/SysUserMapper.xml @@ -1,22 +1,4 @@ - @@ -28,203 +10,131 @@ + + + - - + + - - - - - - + + + select="com.pig4cloud.pigx.admin.mapper.SysRoleMapper.listRolesByUserId" + column="{userId=user_id, tenantId=tenant_id}"> + select="com.pig4cloud.pigx.admin.mapper.SysPostMapper.listPostsByUserId" + column="{userId=user_id, tenantId=tenant_id}"> - + u.user_id, u.username, u.password, u.salt, u.phone, u.avatar, - u.wx_openid, - u.qq_openid, - u.gitee_login, - u.osc_id, - u.wx_cp_userid, - u.wx_ding_userid, - u.dept_id, - u.del_flag, - u.lock_flag, - u.password_expire_flag, - u.password_modify_time, - u.tenant_id, - u.create_by, - u.create_time ucreate_time, - u.update_time uupdate_time, - r.role_id, - r.role_name, - r.role_code, - r.role_desc, - r.create_time rcreate_time, - r.update_time rupdate_time - - - - u.user_id, - u.username, - u.password, - u.salt, - u.phone, - u.avatar, - u.wx_openid, - u.qq_openid, - u.gitee_login, - u.osc_id, - u.wx_cp_userid, - u.wx_ding_userid, - u.del_flag, - u.lock_flag, - u.password_expire_flag, - u.password_modify_time, - u.tenant_id, - u.nickname, u.name, + u.nickname, u.email, + u.wx_openid, + u.qq_openid, + u.gitee_login, + u.osc_id, + u.wx_cp_userid, + u.wx_ding_userid, + u.del_flag, + u.lock_flag, + u.password_expire_flag, + u.password_modify_time, + u.tenant_id, u.create_by, - u.create_time ucreate_time, - u.update_time uupdate_time, - d.name dept_name, - d.dept_id + u.create_time, + u.update_time