-
修改用户表:sys_user 增加 tenant_id(租户ID,注意实体增加对应字段)
-
修改 MybatisPlusSaasConfig 注意 注意 注意 :《MybatisPlusConfig 中租户插件的顺序必须在分页插件之前,否则会有分页total count查询sql没有追加tenant_id的条件的坑坑》
-
修改登录接口 LoginController ,用户查询 ,登录检查用户是否有效不添加租户的附加条件(租户ID)
接口ISysUserService新增:
/**
* 用户登录查询,不进行SQL解析,不添加租户的附加条件
* @param userName
* @return
*/
SysUser getOneUser(String userName);
实现类SysUserServiceImpl新增:
/**
* 用户登录查询,不进行SQL解析,不添加租户的附加条件
* @param userName
* @return
*/
@Override
public SysUser getOneUser(String userName) {
return userMapper.getOneUser(userName);
}
修改SysUserMapper增加:
/**
* 用户登录查询,不进行SQL解析,不添加租户的附加条件
* @param userName
* @return
*/
@SqlParser(filter = true)
SysUser getOneUser(String userName);
修改SysUserMapper.xml 增加:
<!--用户登录查询,不进行SQL解析,不添加租户的附加条件-->
<select id="getOneUser" parameterType="string" resultType="org.jeecg.modules.system.entity.SysUser">
select * from sys_user where username = #{username} and del_flag = 0
</select>
修改SysUserController 新增用户方法,新增用户时,将当前登录用户的tenantId值赋给新用户:
@RequestMapping(value = "/add", method = RequestMethod.POST)
public Result<SysUser> add(@RequestBody JSONObject jsonObject,HttpServletRequest req) {
Result<SysUser> result = new Result<SysUser>();
String selectedRoles = jsonObject.getString("selectedroles");
String selectedDeparts = jsonObject.getString("selecteddeparts");
try {
String tenantId = req.getHeader("tenant-id");
SysUser user = JSON.parseObject(jsonObject.toJSONString(), SysUser.class);
user.setCreateTime(new Date());//设置创建时间
String salt = oConvertUtils.randomGen(8);
user.setSalt(salt);
String passwordEncode = PasswordUtil.encrypt(user.getUsername(), user.getPassword(), salt);
user.setPassword(passwordEncode);
user.setStatus(1);
user.setDelFlag(CommonConstant.DEL_FLAG_0);
user.setRelTenantIds(tenantId);
user.setTenantId(Integer.valueOf(tenantId));
sysUserService.addUserWithRole(user, selectedRoles);
sysUserService.addUserWithDepart(user, selectedDeparts);
result.success("添加成功!");
} catch (Exception e) {
log.error(e.getMessage(), e);
result.error500("操作失败");
}
return result;
}
将前端用户管理新增界面的选择租户删除,新增用户所属租户ID在后端处理,处理方式如上。
修改SysPermissionController 查询用户拥有的菜单权限和按钮权限getUserPermissionByToken方法:
/**
* 查询用户拥有的菜单权限和按钮权限
*
* @return
*/
@RequestMapping(value = "/getUserPermissionByToken", method = RequestMethod.GET)
public Result<?> getUserPermissionByToken(HttpServletRequest req) {
Result<JSONObject> result = new Result<JSONObject>();
try {
//直接获取当前用户不适用前端token
LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
if (oConvertUtils.isEmpty(loginUser)) {
return Result.error("请登录系统!");
}
List<SysPermission> metaList = null;
//检查是否为超级管理员,超级管理员下有多个租户的菜单,只查当前租户菜单
//判断当前登录用户是否为超级管理员,具体判断方式可根据自身项目情况使用相对应判断方法
if(JudgeEnum.YES.getValue().equals(loginUser.getUserFlag())){
String tenantId = req.getHeader("tenant-id");
metaList = sysPermissionService.queryPermissionByUser(loginUser.getUsername(),tenantId);
}else {
metaList = sysPermissionService.queryByUser(loginUser.getUsername());
}
//添加首页路由
//update-begin-author:taoyan date:20200211 for: TASK #3368 【路由缓存】首页的缓存设置有问题,需要根据后台的路由配置来实现是否缓存
if(!PermissionDataUtil.hasIndexPage(metaList)){
SysPermission indexMenu = sysPermissionService.list(new LambdaQueryWrapper<SysPermission>().eq(SysPermission::getName,"首页")).get(0);
metaList.add(0,indexMenu);
}
//update-end-author:taoyan date:20200211 for: TASK #3368 【路由缓存】首页的缓存设置有问题,需要根据后台的路由配置来实现是否缓存
JSONObject json = new JSONObject();
JSONArray menujsonArray = new JSONArray();
this.getPermissionJsonArray(menujsonArray, metaList, null);
JSONArray authjsonArray = new JSONArray();
this.getAuthJsonArray(authjsonArray, metaList);
//查询所有的权限
LambdaQueryWrapper<SysPermission> query = new LambdaQueryWrapper<SysPermission>();
query.eq(SysPermission::getDelFlag, CommonConstant.DEL_FLAG_0);
query.eq(SysPermission::getMenuType, CommonConstant.MENU_TYPE_2);
//query.eq(SysPermission::getStatus, "1");
List<SysPermission> allAuthList = sysPermissionService.list(query);
JSONArray allauthjsonArray = new JSONArray();
this.getAllAuthJsonArray(allauthjsonArray, allAuthList);
//路由菜单
json.put("menu", menujsonArray);
//按钮权限(用户拥有的权限集合)
json.put("auth", authjsonArray);
//全部权限配置集合(按钮权限,访问权限)
json.put("allAuth", allauthjsonArray);
result.setResult(json);
result.success("查询成功");
} catch (Exception e) {
result.error500("查询失败:" + e.getMessage());
log.error(e.getMessage(), e);
}
return result;
}
修改代码:
//检查是否为超级管理员,超级管理员下有多个租户的菜单,只查当前租户菜单
//判断当前登录用户是否为超级管理员,具体判断方式可根据自身项目情况使用相对应判断方法
if(JudgeEnum.YES.getValue().equals(loginUser.getUserFlag())){
String tenantId = req.getHeader("tenant-id");
metaList = sysPermissionService.queryPermissionByUser(loginUser.getUsername(),tenantId);
}else {
metaList = sysPermissionService.queryByUser(loginUser.getUsername());
}
接口类ISysPermissionService 新增:
/**
* 查询租户菜单,不走租户ID附件条件,针对超级管理员admin
* @param username
* @return
*/
public List<SysPermission> queryPermissionByUser(String username,String tenantId);
实现类 SysPermissionServiceImpl 新增:
/**
* 查询租户菜单,不走租户ID附件条件,针对超级管理员admin
* @param username
* @return
*/
@Override
public List<SysPermission> queryPermissionByUser(String username,String tenantId) {
return this.sysPermissionMapper.queryPermissionByUser(username,tenantId);
}
修改 SysPermissionMapper 新增代码:
/**
* 查询租户菜单,不走租户ID附件条件,针对超级管理员admin
* @param username
* @return
*/
@SqlParser(filter = true)
public List<SysPermission> queryPermissionByUser(@Param("username") String username,@Param("tenantId") String tenantId);
修改 SysPermissionMapper.xml 新增代码:
<!--查询租户菜单,不走租户ID附件条件,针对超级管理员admin-->
<select id="queryPermissionByUser" parameterType="Object" resultMap="SysPermission">
SELECT * FROM (
SELECT p.*
FROM sys_permission p
WHERE (exists(
select a.id from sys_role_permission a
join sys_role b on a.role_id = b.id
join sys_user_role c on c.role_id = b.id
join sys_user d on d.id = c.user_id
where p.id = a.permission_id AND d.username = #{username} and p.tenant_id=#{tenantId}
)
or (p.url like '%:code' and p.url like '/online%' and p.hidden = 1)
or p.url = '/online')
and p.del_flag = 0
<!--update begin Author:lvdandan Date:20200213 for:加入部门权限 -->
UNION
SELECT p.*
FROM sys_permission p
WHERE exists(
select a.id from sys_depart_role_permission a
join sys_depart_role b on a.role_id = b.id
join sys_depart_role_user c on c.drole_id = b.id
join sys_user d on d.id = c.user_id
where p.id = a.permission_id AND d.username = #{username} and p.tenant_id=#{tenantId}
)
and p.del_flag = 0
<!--update end Author:lvdandan Date:20200213 for:加入部门权限 -->
) h order by h.sort_no ASC
</select>
修改 ShiroRealm中checkUserTokenIsEffect方法:
注:此处修改目的,解决超级管理员用户admin登录报错登录已过期
但其他租户用户登录均能正常登录
接口类 CommonAPI 新增:
/**
* 用户登录查询,不进行SQL解析,不添加租户的附加条件
* @param username
* @return
*/
LoginUser getOneUser(String username);
实现类 SysBaseApiImpl 新增:
@Override
public LoginUser getOneUser(String username) {
SysUser user = userMapper.getOneUser(username);
LoginUser loginUser = new LoginUser();
BeanUtil.copyProperties(user,loginUser);
return loginUser;
}
修改 SysUserMapper 新增代码:
/**
* 用户登录查询,不进行SQL解析,不添加租户的附加条件
* @param userName
* @return
*/
@SqlParser(filter = true)
SysUser getOneUser(String userName);
修改 SysUserMapper.xml 新增代码:
<!--用户登录查询,不进行SQL解析,不添加租户的附加条件-->
<select id="getOneUser" parameterType="string" resultType="org.jeecg.modules.system.entity.SysUser">
select * from sys_user where username = #{username} and del_flag = 0
</select>
****配置过滤过滤器,若不用后面的TenantHandlerInterceptor.java 此处可省略 ** **
import org.jeecg.filter.MyHttpServletRequestFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
/**
* @Author jianghx
* @Date 2022/3/8 12:45
* @Version 1.0
**/
@Component
public class FilterConfiguration {
/**
* 注册过滤器bean MyHttpServletRequestFilter
* 针对于登录接口 /sys/login、/sys/mLogin
* 登录接口在拦截器(TenantHandlerInterceptor)中会取出request中的body参数
* @return
*/
@Bean
public FilterRegistrationBean MyHttpServletRequestFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new MyHttpServletRequestFilter());
//登录接口 url ,具体url 根据项目进行相应修改
registration.addUrlPatterns("/sys/login","/sys/mLogin");
registration.setName("MyHttpServletRequestFilter");
registration.setOrder(1000);
return registration;
}
}
MyPathMatchingFilterChainResolver.java
import org.apache.shiro.util.PatternMatcher;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.springframework.stereotype.Component;
import javax.servlet.ServletRequest;
/**
* @Author jianghx
* @Date 2022/3/3 15:15
* @Version 1.0
**/
@Component
public class MyPathMatchingFilterChainResolver extends PathMatchingFilterChainResolver {
public String getMyPathWithinApplication(ServletRequest request){
return super.getPathWithinApplication(request);
}
public boolean MyPathMatches2(String pattern, String path) {
return super.pathMatches(pattern,path);
}
}
MyHttpServletRequestFilter.java 解决request获取body参数时只能获取一次
import org.jeecg.util.HttpContextUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
/**
*
* 解决request获取body参数时只能获取一次,
* 此处只针对登录接口,原因 登录接口在拦截器(TenantHandlerInterceptor)中会取出request中的body参数
*/
public class MyHttpServletRequestFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
ServletRequest requestWrapper = null;
if(servletRequest instanceof HttpServletRequest) {
requestWrapper = new RequestWrapper((HttpServletRequest) servletRequest);
}
//获取请求中的流如何,将取出来的字符串,再次转换成流,然后把它放入到新request对象中
// 在chain.doFiler方法中传递新的request对象
if(null == requestWrapper) {
filterChain.doFilter(servletRequest, servletResponse);
} else {
filterChain.doFilter(requestWrapper, servletResponse);
}
}
@Override
public void destroy() {
}
/***
* HttpServletRequest 包装器
* 解决: request.getInputStream()只能读取一次的问题
* 目标: 流可重复读
*/
public class RequestWrapper extends HttpServletRequestWrapper {
/**
* 请求体
*/
private String mBody;
public RequestWrapper(HttpServletRequest request) {
super(request);
// 将body数据存储起来
mBody = getBody(request);
}
/**
* 获取请求体
* @param request 请求
* @return 请求体
*/
private String getBody(HttpServletRequest request) {
return HttpContextUtils.getBodyString(request);
}
/**
* 获取请求体
* @return 请求体
*/
public String getBody() {
return mBody;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public ServletInputStream getInputStream() throws IOException {
// 创建字节数组输入流
final ByteArrayInputStream bais = new ByteArrayInputStream(mBody.getBytes(StandardCharsets.UTF_8));
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
return bais.read();
}
};
}
}
}
添加租户登录拦截器TenantHandlerInterceptor.java,在用户登录时对租户状态(是否冻结)、租户是否到期进行检查,可根据自身情况决定是否添加
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.JsonObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.modules.scmapprovalflow.enums.JudgeEnum;
import org.jeecg.modules.system.entity.SysTenant;
import org.jeecg.modules.system.entity.SysUser;
import org.jeecg.modules.system.model.SysLoginModel;
import org.jeecg.modules.system.service.ISysTenantService;
import org.jeecg.modules.system.service.ISysUserService;
import org.jeecg.util.HttpContextUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
/**
* 针对登录接口进行租户登录检查
*
* @Author jianghx
* @Date 2022/3/3 11:26
* @Version 1.0
**/
@Component
@Slf4j
@Configuration
public class TenantHandlerInterceptor extends HandlerInterceptorAdapter {
//yml文件配置登录接口 /sys/login,/sys/mLogin
@Value("${jeecg.shiro.loginUrlPatterns}")
private String loginUrlPatterns;
@Autowired
private ISysTenantService sysTenantService;
@Autowired
private ISysUserService sysUserService;
@Autowired
private MyPathMatchingFilterChainResolver myPathMatchingFilterChainResolver;
/**
* 针对登录接口进行租户状态、租户时间检查
* tenantCheckInterceptUrls: /sys/login,/sys/mLogin
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String[] urls = loginUrlPatterns.split(",");
if(urls.length>0 ){
String tenantId = request.getHeader(CommonConstant.TENANT_ID);
//获取当前请求url
final String requestURI = myPathMatchingFilterChainResolver.getMyPathWithinApplication(request);
//检查当前请求是否在shirod filterChainDefinitionMap 过滤路径内
//此处url判断是从滤器源码中复制过来的,与过滤器判断方法保持一致,
for (String url : urls) {
if (myPathMatchingFilterChainResolver.MyPathMatches2(url, requestURI) && StringUtils.isNotBlank(tenantId)) {
String bodyString = HttpContextUtils.getBodyString(request);
if (StringUtils.isNotBlank(loginUrlPatterns) && StringUtils.isNotBlank(bodyString)) {
JSONObject jsonObject = JSONObject.parseObject(bodyString);
String userName = jsonObject.getString("username");
//查询用户
SysUser userInfo = sysUserService.getOneUser(userName);
//超级用户登录,不进行过期验证
if(ObjectUtil.isNotNull(userInfo)
&& JudgeEnum.YES.getValue().equals(userInfo.getUserFlag())){
return true;
}
if(ObjectUtil.isNull(userInfo)){
throw new NullPointerException("未查询到用户信息!");
}
SysTenant tenantInfo = sysTenantService.getById(userInfo.getTenantId());
// 租户状态状态 1正常 0冻结
if(tenantInfo.getStatus() == JudgeEnum.NO.getValue()){
throw new RuntimeException("租户已冻结,请联系管理员!");
}
if (null == tenantInfo.getBeginDate() || null == tenantInfo.getEndDate()) {
log.error("租户信息", tenantInfo);
log.info("===============tenantId=========" + tenantId);
log.error("当前租户开始时间、结束时间不能有null存在!");
throw new NullPointerException("参数缺失,请联系管理员");
}
//检查租户到期时间
if (null != tenantInfo.getEndDate()) {
Date tenantEndDate = tenantInfo.getEndDate();//租户到期时间
int val = DateUtil.compare(tenantEndDate, new Date());
if (val < 0) {
throw new RuntimeException("租户时间已到期!");
}
}
}
}
}
}
return true;
}
}
前端部分
TenantForm.vue 文件,新增租户页面添加角色前缀编码字段
UserModal.vue调整
多租户使用说明:
前提条件:
参考本文及官方介绍,完成用户、部门、角色、菜单多租户代码构建。
-
使用admin账号,在租户管理添加新租户(添加页面的角色编码前缀 用于新租户admin角色前缀)
-
新增租户时,会默认创建新租户菜单、角色的关联关系,且会创建新租户的admin角色(角色前缀_admin ,如:角色前缀 tenant1,新租户admin角色 = tenant1_admin)
-
退出admin,重新登录,选择新增租户登录进去
-
登录成功之后,创建部门,并为新租户的admin用户及其角色 分配相关权限及菜单(admin 角色在新增租户时已创建)
至此完成用户、角色、菜单、部门的多租户改造
Jeecg-boot 官方系统多租户角色、部门、菜单https://jeecg.blog.csdn.net/article/details/119780074
Jeecg-boot 官方系统多租户角色、部门、菜单
=以下是存在BUG解决============
0. 防止不同租户创建出相同用户账号从而引发出的安全问题
-
解决方式一:登录界面添加登录登录要素【租户编号】,即登录时需要输入租户编号、账号、密码三个要素
-
解决方式二:创建用户时,除了超级管理员(admin)以外,其他用户创建用户需要添加格式校验【租户前缀+用户账号,例如:租户前缀 为:zh,用户格式则为:zhLis,可根据自身情况调整】。租户前缀在租户管理新增时有添加且唯一。
账号前缀部分会随租户的不同而有所变化 -
具体实现:
1). 修改mutation-types.js
export const TENANT_INFO = 'Login_TenantInfo'
import { loginTenant} from "@/api/login"
2)修改LoginSelectTenant.vue
import { USER_INFO,TENANT_INFO } from "@/store/mutation-types"
- List item
selectOk方法中加入租户信息查询的方法
loginTenant({tenantId:this.tenant_id}).then((response)=>{
if(response.code =='200'){
const result = response.result
const tenantInfo = result.tenantInfo
Vue.ls.set(TENANT_INFO, tenantInfo, 7 * 24 * 60 * 60 * 1000)
commit('SET_INFO', tenantInfo)
resolve();
}else{
return
}
})
- login.js
export function loginTenant(data) {
return axios({
url: '/sys/loginTenant',
method: 'get',
params: data
})
}
- LoginController.java
@ApiOperation("登录接口获取租户信息")
@RequestMapping(value = "/loginTenant", method = RequestMethod.GET)
public Result<JSONObject> loginTenant(@RequestParam(name = "tenantId", required = true) Integer tenantId){
Result<JSONObject> result = new Result<JSONObject>();
JSONObject obj = new JSONObject();
//租户信息
if(ObjectUtil.isNotNull(tenantId) && null != tenantId) {
SysTenant sysTenant = sysTenantService.getById(tenantId);
obj.put("tenantInfo", sysTenant);
result.setResult(obj);
result.success("成功");
}else{
result.error500("参数缺失");
}
return result;
}
- 修改UserModal.vue
import { ACCESS_TOKEN,TENANT_INFO } from "@/store/mutation-types"
placeholderUserName:null
- 修改用户账户输入框账号格式动态提示
<a-form-model-item label="用户账号" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="username">
<a-input :placeholder="placeholderUserName" label-suffix="sss" v-model="model.username" :readOnly="!!model.id"/>
</a-form-model-item>
- 修改【新增按钮】方法
add () {
const tenantInfo = Vue.ls.get(TENANT_INFO);
this.refresh();
this.placeholderUserName = '账号格式 ['+tenantInfo.preCode+'XXX],例:'+tenantInfo.preCode+'ZhangSan';
this.edit({activitiSync:'1',userIdentity:1});
},
- 修改提交保存方法(handleSubmit),添加账号格式校验
this.model.id = this.userId;
const tenantInfo = Vue.ls.get(TENANT_INFO);
let preCode = tenantInfo.preCode;//账号前缀
if('admin' != this.model.username && (this.model.username.length<=preCode.length
||this.model.username.substring(0,preCode.length) != preCode)){
that.confirmLoading = false;
that.$message.warning("账号格式错误!");
return;
}
- 超级管理(admin)所属租户修改界面角色显示问题 - 问题描述:admin账户是归属租户A(admin在租户B、C也存在角色关系),但在租户A的用户编辑界面会显示其他租户的角色.
- 原因:此处的角色查询方法没有添加租户ID条件
- 解决办法:修改查询方法(/sys/user/queryUserRole)
修改SysUserController.java
@RequestMapping(value = "/queryUserRole", method = RequestMethod.GET)
public Result<List<String>> queryUserRole(@RequestParam(name = "userid", required = true) String userid) {
Result<List<String>> result = new Result<>();
List<String> list = new ArrayList<String>();
// List<SysUserRole> userRole = sysUserRoleService.list(new QueryWrapper<SysUserRole>().lambda().eq(SysUserRole::getUserId, userid));
List<SysUserRole> userRole = userRoleMapper.listUserRoleByUserId(userid);
if (userRole == null || userRole.size() <= 0) {
result.error500("未找到用户相关角色信息");
} else {
for (SysUserRole sysUserRole : userRole) {
list.add(sysUserRole.getRoleId());
}
result.setSuccess(true);
result.setResult(list);
}
return result;
}
修改SysUserRoleMapper.java,添加查询方法
/**
* 查询角色信息
* @param userId
* @return
*/
@Select("SELECT t.id, t.user_id, t.role_id FROM sys_user_role t join sys_role u on u.id = t.role_id WHERE (t.user_id = #{userId} ) ")
List<SysUserRole> listUserRoleByUserId(@Param("userId") String userId);
- 超级管理员(admim) ,用户编辑保存之后(内容上不做任何修改),退出重新登录,没有弹出租户选择的下拉界面
-
原因:在用户编辑方法中,会重新给sys_user表中的rel_tenant_ids字段重新更新值(更新成当前登录租户ID),导致admin重登录时,只有一个租户。非admin用户rel_tenant_ids字段只有一个值,admin是多个租户id
-
解决方法,在用户保存方法中,将rel_tenant_ids赋值操作删除。
SysUserController
3. 通讯录管理查询修改
- 问题描述:由于查询为多表关联查询,但是原sql没有使用别名,导致多个表的租户ID tenant_id,也没有别名从而查询方法直接报错
修改SysUserMapper.xml
<!-- SQL片段:getUserByOrgCode 的 FROM 和 WHERE 部分 -->
<sql id="getUserByOrgCodeFromSql">
FROM
sys_depart d
INNER JOIN sys_user_depart sud ON sud.dep_id = d.id
INNER JOIN sys_user u ON u.id = sud.user_id
WHERE
u.del_flag = 0 AND d.org_code LIKE '${orgCode}%'
<if test="userParams != null">
<if test="userParams.realname != null and userParams.realname != ''">
AND u.realname LIKE concat(concat('%',#{userParams.realname}),'%')
</if>
<if test="userParams.workNo != null and userParams.workNo != ''">
AND u.work_no LIKE concat(concat('%',#{userParams.workNo}),'%')
</if>
</if>
</sql>
<!-- 根据 orgCode 查询用户,包括子部门下的用户 -->
<select id="getUserByOrgCode" resultType="org.jeecg.modules.system.model.SysUserSysDepartModel">
SELECT
u.id AS id,
u.realname AS realname,
u.avatar AS avatar,
u.work_no AS workNo,
u.post AS post,
u.telephone AS telephone,
u.email AS email,
u.phone AS phone,
d.id AS departId,
d.depart_name AS departName
<include refid="getUserByOrgCodeFromSql"/>
ORDER BY
d.org_code ASC
</select>