mirror of
				https://github.com/dromara/RuoYi-Vue-Plus.git
				synced 2025-11-04 16:23:42 +08:00 
			
		
		
		
	!286 合并 多租户功能
* add 新增 ruoyi-common-tenant 多租户模块 全框架适配多租户改动 * update 优化 隐藏页面主键 * remove 移除 缓存列表功能(多租户缓存功能繁杂多样 没有办法在页面管理) * update 重构 全局缓存KEY 与 常用缓存KEY做区分 * update 重构 OssFactory 加载方式 改为每次比对配置做实例更新 * update 优化 SaTokenDao 改为 Bean 注入 便于扩展 * update 重构 项目初始化数据改为懒加载 不提供热加载 * update 重构 验证码开关使用配置文件(经调查少有动态开启需求) * update 优化 启用 sqlserver 高版本语法 简化sql脚本语法 * update 优化 DataPermissionHelper 增加 开启/关闭 忽略数据权限功能 * update 优化 连接池增加 keepaliveTime 探活参数 * update 优化 调整连接池最长生命周期 防止出现警告 * update 优化 代码生成页面模板 校验不必要的表单数据 * add 新增 StringUtils splitTo 与 splitList 方法 优化业务代码
This commit is contained in:
		
							
								
								
									
										6
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								pom.xml
									
									
									
									
									
								
							@@ -30,7 +30,7 @@
 | 
			
		||||
        <hutool.version>5.8.11</hutool.version>
 | 
			
		||||
        <okhttp.version>4.10.0</okhttp.version>
 | 
			
		||||
        <spring-boot-admin.version>3.0.0</spring-boot-admin.version>
 | 
			
		||||
        <redisson.version>3.19.1</redisson.version>
 | 
			
		||||
        <redisson.version>3.19.2</redisson.version>
 | 
			
		||||
        <lock4j.version>2.2.4</lock4j.version>
 | 
			
		||||
        <dynamic-ds.version>3.6.1</dynamic-ds.version>
 | 
			
		||||
        <alibaba-ttl.version>2.14.2</alibaba-ttl.version>
 | 
			
		||||
@@ -42,10 +42,10 @@
 | 
			
		||||
        <snakeyaml.version>1.33</snakeyaml.version>
 | 
			
		||||
 | 
			
		||||
        <!-- OSS 配置 -->
 | 
			
		||||
        <aws-java-sdk-s3.version>1.12.373</aws-java-sdk-s3.version>
 | 
			
		||||
        <aws-java-sdk-s3.version>1.12.400</aws-java-sdk-s3.version>
 | 
			
		||||
        <!-- SMS 配置 -->
 | 
			
		||||
        <aliyun.sms.version>2.0.23</aliyun.sms.version>
 | 
			
		||||
        <tencent.sms.version>3.1.660</tencent.sms.version>
 | 
			
		||||
        <tencent.sms.version>3.1.687</tencent.sms.version>
 | 
			
		||||
 | 
			
		||||
        <!-- 插件版本 -->
 | 
			
		||||
        <maven-jar-plugin.version>3.2.2</maven-jar-plugin.version>
 | 
			
		||||
 
 | 
			
		||||
@@ -5,8 +5,8 @@ import cn.hutool.captcha.AbstractCaptcha;
 | 
			
		||||
import cn.hutool.captcha.generator.CodeGenerator;
 | 
			
		||||
import cn.hutool.core.util.IdUtil;
 | 
			
		||||
import cn.hutool.core.util.RandomUtil;
 | 
			
		||||
import com.ruoyi.common.core.constant.CacheConstants;
 | 
			
		||||
import com.ruoyi.common.core.constant.Constants;
 | 
			
		||||
import com.ruoyi.common.core.constant.GlobalConstants;
 | 
			
		||||
import com.ruoyi.common.core.domain.R;
 | 
			
		||||
import com.ruoyi.common.core.utils.SpringUtils;
 | 
			
		||||
import com.ruoyi.common.core.utils.StringUtils;
 | 
			
		||||
@@ -17,8 +17,8 @@ import com.ruoyi.common.sms.core.SmsTemplate;
 | 
			
		||||
import com.ruoyi.common.sms.entity.SmsResult;
 | 
			
		||||
import com.ruoyi.common.web.config.properties.CaptchaProperties;
 | 
			
		||||
import com.ruoyi.common.web.enums.CaptchaType;
 | 
			
		||||
import com.ruoyi.system.service.ISysConfigService;
 | 
			
		||||
import com.ruoyi.web.domain.vo.CaptchaVo;
 | 
			
		||||
import jakarta.validation.constraints.NotBlank;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.expression.Expression;
 | 
			
		||||
@@ -28,7 +28,6 @@ import org.springframework.validation.annotation.Validated;
 | 
			
		||||
import org.springframework.web.bind.annotation.GetMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.RestController;
 | 
			
		||||
 | 
			
		||||
import jakarta.validation.constraints.NotBlank;
 | 
			
		||||
import java.time.Duration;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
@@ -47,7 +46,6 @@ public class CaptchaController {
 | 
			
		||||
 | 
			
		||||
    private final CaptchaProperties captchaProperties;
 | 
			
		||||
    private final SmsProperties smsProperties;
 | 
			
		||||
    private final ISysConfigService configService;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 短信验证码
 | 
			
		||||
@@ -60,7 +58,7 @@ public class CaptchaController {
 | 
			
		||||
        if (!smsProperties.getEnabled()) {
 | 
			
		||||
            return R.fail("当前系统没有开启短信功能!");
 | 
			
		||||
        }
 | 
			
		||||
        String key = CacheConstants.CAPTCHA_CODE_KEY + phonenumber;
 | 
			
		||||
        String key = GlobalConstants.CAPTCHA_CODE_KEY + phonenumber;
 | 
			
		||||
        String code = RandomUtil.randomNumbers(4);
 | 
			
		||||
        RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
 | 
			
		||||
        // 验证码模板id 自行处理 (查数据库或写死均可)
 | 
			
		||||
@@ -82,14 +80,14 @@ public class CaptchaController {
 | 
			
		||||
    @GetMapping("/captchaImage")
 | 
			
		||||
    public R<CaptchaVo> getCode() {
 | 
			
		||||
        CaptchaVo captchaVo = new CaptchaVo();
 | 
			
		||||
        boolean captchaEnabled = configService.selectCaptchaEnabled();
 | 
			
		||||
        boolean captchaEnabled = captchaProperties.getEnable();
 | 
			
		||||
        if (!captchaEnabled) {
 | 
			
		||||
            captchaVo.setCaptchaEnabled(false);
 | 
			
		||||
            return R.ok(captchaVo);
 | 
			
		||||
        }
 | 
			
		||||
        // 保存验证码信息
 | 
			
		||||
        String uuid = IdUtil.simpleUUID();
 | 
			
		||||
        String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;
 | 
			
		||||
        String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + uuid;
 | 
			
		||||
        // 生成验证码
 | 
			
		||||
        CaptchaType captchaType = captchaProperties.getType();
 | 
			
		||||
        boolean isMath = CaptchaType.MATH == captchaType;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,19 +1,27 @@
 | 
			
		||||
package com.ruoyi.web.controller;
 | 
			
		||||
 | 
			
		||||
import cn.dev33.satoken.annotation.SaIgnore;
 | 
			
		||||
import cn.hutool.core.bean.BeanUtil;
 | 
			
		||||
import cn.hutool.core.collection.CollUtil;
 | 
			
		||||
import com.ruoyi.common.core.domain.R;
 | 
			
		||||
import com.ruoyi.common.core.domain.model.LoginBody;
 | 
			
		||||
import com.ruoyi.common.core.domain.model.LoginUser;
 | 
			
		||||
import com.ruoyi.common.core.domain.model.SmsLoginBody;
 | 
			
		||||
import com.ruoyi.common.core.utils.StringUtils;
 | 
			
		||||
import com.ruoyi.common.satoken.utils.LoginHelper;
 | 
			
		||||
import com.ruoyi.system.domain.SysMenu;
 | 
			
		||||
import com.ruoyi.system.domain.bo.SysTenantBo;
 | 
			
		||||
import com.ruoyi.system.domain.vo.RouterVo;
 | 
			
		||||
import com.ruoyi.system.domain.vo.SysTenantVo;
 | 
			
		||||
import com.ruoyi.system.domain.vo.SysUserVo;
 | 
			
		||||
import com.ruoyi.system.service.ISysMenuService;
 | 
			
		||||
import com.ruoyi.system.service.ISysTenantService;
 | 
			
		||||
import com.ruoyi.system.service.ISysUserService;
 | 
			
		||||
import com.ruoyi.system.service.SysLoginService;
 | 
			
		||||
import com.ruoyi.web.domain.vo.LoginVo;
 | 
			
		||||
import com.ruoyi.web.domain.vo.TenantListVo;
 | 
			
		||||
import com.ruoyi.web.domain.vo.UserInfoVo;
 | 
			
		||||
import com.ruoyi.web.service.SysLoginService;
 | 
			
		||||
import jakarta.servlet.http.HttpServletRequest;
 | 
			
		||||
import jakarta.validation.constraints.NotBlank;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import org.springframework.validation.annotation.Validated;
 | 
			
		||||
@@ -22,6 +30,7 @@ import org.springframework.web.bind.annotation.PostMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.RequestBody;
 | 
			
		||||
import org.springframework.web.bind.annotation.RestController;
 | 
			
		||||
 | 
			
		||||
import java.net.URL;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -37,20 +46,23 @@ public class SysLoginController {
 | 
			
		||||
    private final SysLoginService loginService;
 | 
			
		||||
    private final ISysMenuService menuService;
 | 
			
		||||
    private final ISysUserService userService;
 | 
			
		||||
    private final ISysTenantService tenantService;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 登录方法
 | 
			
		||||
     *
 | 
			
		||||
     * @param loginBody 登录信息
 | 
			
		||||
     * @param body 登录信息
 | 
			
		||||
     * @return 结果
 | 
			
		||||
     */
 | 
			
		||||
    @SaIgnore
 | 
			
		||||
    @PostMapping("/login")
 | 
			
		||||
    public R<LoginVo> login(@Validated @RequestBody LoginBody loginBody) {
 | 
			
		||||
    public R<LoginVo> login(@Validated @RequestBody LoginBody body) {
 | 
			
		||||
        LoginVo loginVo = new LoginVo();
 | 
			
		||||
        // 生成令牌
 | 
			
		||||
        String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
 | 
			
		||||
            loginBody.getUuid());
 | 
			
		||||
        String token = loginService.login(
 | 
			
		||||
                body.getTenantId(),
 | 
			
		||||
                body.getUsername(), body.getPassword(),
 | 
			
		||||
                body.getCode(), body.getUuid());
 | 
			
		||||
        loginVo.setToken(token);
 | 
			
		||||
        return R.ok(loginVo);
 | 
			
		||||
    }
 | 
			
		||||
@@ -58,15 +70,15 @@ public class SysLoginController {
 | 
			
		||||
    /**
 | 
			
		||||
     * 短信登录(示例)
 | 
			
		||||
     *
 | 
			
		||||
     * @param smsLoginBody 登录信息
 | 
			
		||||
     * @param body 登录信息
 | 
			
		||||
     * @return 结果
 | 
			
		||||
     */
 | 
			
		||||
    @SaIgnore
 | 
			
		||||
    @PostMapping("/smsLogin")
 | 
			
		||||
    public R<LoginVo> smsLogin(@Validated @RequestBody SmsLoginBody smsLoginBody) {
 | 
			
		||||
    public R<LoginVo> smsLogin(@Validated @RequestBody SmsLoginBody body) {
 | 
			
		||||
        LoginVo loginVo = new LoginVo();
 | 
			
		||||
        // 生成令牌
 | 
			
		||||
        String token = loginService.smsLogin(smsLoginBody.getPhonenumber(), smsLoginBody.getSmsCode());
 | 
			
		||||
        String token = loginService.smsLogin(body.getTenantId(), body.getPhonenumber(), body.getSmsCode());
 | 
			
		||||
        loginVo.setToken(token);
 | 
			
		||||
        return R.ok(loginVo);
 | 
			
		||||
    }
 | 
			
		||||
@@ -97,6 +109,23 @@ public class SysLoginController {
 | 
			
		||||
        return R.ok("退出成功");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 登录页面租户下拉框
 | 
			
		||||
     *
 | 
			
		||||
     * @return 租户列表
 | 
			
		||||
     */
 | 
			
		||||
    @SaIgnore
 | 
			
		||||
    @GetMapping("/tenant/list")
 | 
			
		||||
    public R<List<TenantListVo>> tenantList(HttpServletRequest request) throws Exception {
 | 
			
		||||
        List<SysTenantVo> tenantList = tenantService.queryList(new SysTenantBo());
 | 
			
		||||
        List<TenantListVo> voList = BeanUtil.copyToList(tenantList, TenantListVo.class);
 | 
			
		||||
        // 获取域名
 | 
			
		||||
        String host = new URL(request.getRequestURL().toString()).getHost();
 | 
			
		||||
        // 根据域名进行筛选
 | 
			
		||||
        List<TenantListVo> list = voList.stream().filter(vo -> StringUtils.equals(vo.getDomain(), host)).toList();
 | 
			
		||||
        return R.ok(CollUtil.isNotEmpty(list) ? list : voList);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取用户信息
 | 
			
		||||
     *
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ import com.ruoyi.common.web.core.BaseController;
 | 
			
		||||
import com.ruoyi.common.core.domain.R;
 | 
			
		||||
import com.ruoyi.common.core.domain.model.RegisterBody;
 | 
			
		||||
import com.ruoyi.system.service.ISysConfigService;
 | 
			
		||||
import com.ruoyi.system.service.SysRegisterService;
 | 
			
		||||
import com.ruoyi.web.service.SysRegisterService;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import org.springframework.validation.annotation.Validated;
 | 
			
		||||
import org.springframework.web.bind.annotation.PostMapping;
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,19 @@
 | 
			
		||||
package com.ruoyi.web.domain.vo;
 | 
			
		||||
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 租户列表
 | 
			
		||||
 *
 | 
			
		||||
 * @author Lion Li
 | 
			
		||||
 */
 | 
			
		||||
@Data
 | 
			
		||||
public class TenantListVo {
 | 
			
		||||
 | 
			
		||||
    private String tenantId;
 | 
			
		||||
 | 
			
		||||
    private String companyName;
 | 
			
		||||
 | 
			
		||||
    private String domain;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
package com.ruoyi.system.service;
 | 
			
		||||
package com.ruoyi.web.service;
 | 
			
		||||
 | 
			
		||||
import cn.dev33.satoken.exception.NotLoginException;
 | 
			
		||||
import cn.dev33.satoken.secure.BCrypt;
 | 
			
		||||
@@ -6,13 +6,14 @@ import cn.dev33.satoken.stp.StpUtil;
 | 
			
		||||
import cn.hutool.core.bean.BeanUtil;
 | 
			
		||||
import cn.hutool.core.util.ObjectUtil;
 | 
			
		||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 | 
			
		||||
import com.ruoyi.common.core.constant.CacheConstants;
 | 
			
		||||
import com.ruoyi.common.core.constant.Constants;
 | 
			
		||||
import com.ruoyi.common.core.constant.GlobalConstants;
 | 
			
		||||
import com.ruoyi.common.core.domain.dto.RoleDTO;
 | 
			
		||||
import com.ruoyi.common.core.domain.model.LoginUser;
 | 
			
		||||
import com.ruoyi.common.core.domain.model.XcxLoginUser;
 | 
			
		||||
import com.ruoyi.common.core.enums.DeviceType;
 | 
			
		||||
import com.ruoyi.common.core.enums.LoginType;
 | 
			
		||||
import com.ruoyi.common.core.enums.TenantStatus;
 | 
			
		||||
import com.ruoyi.common.core.enums.UserStatus;
 | 
			
		||||
import com.ruoyi.common.core.exception.user.CaptchaException;
 | 
			
		||||
import com.ruoyi.common.core.exception.user.CaptchaExpireException;
 | 
			
		||||
@@ -21,9 +22,15 @@ import com.ruoyi.common.core.utils.*;
 | 
			
		||||
import com.ruoyi.common.log.event.LogininforEvent;
 | 
			
		||||
import com.ruoyi.common.redis.utils.RedisUtils;
 | 
			
		||||
import com.ruoyi.common.satoken.utils.LoginHelper;
 | 
			
		||||
import com.ruoyi.common.tenant.exception.TenantException;
 | 
			
		||||
import com.ruoyi.common.tenant.helper.TenantHelper;
 | 
			
		||||
import com.ruoyi.common.web.config.properties.CaptchaProperties;
 | 
			
		||||
import com.ruoyi.system.domain.SysUser;
 | 
			
		||||
import com.ruoyi.system.domain.vo.SysTenantVo;
 | 
			
		||||
import com.ruoyi.system.domain.vo.SysUserVo;
 | 
			
		||||
import com.ruoyi.system.mapper.SysUserMapper;
 | 
			
		||||
import com.ruoyi.system.service.ISysPermissionService;
 | 
			
		||||
import com.ruoyi.system.service.ISysTenantService;
 | 
			
		||||
import jakarta.servlet.http.HttpServletRequest;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
@@ -31,6 +38,7 @@ import org.springframework.beans.factory.annotation.Value;
 | 
			
		||||
import org.springframework.stereotype.Service;
 | 
			
		||||
 | 
			
		||||
import java.time.Duration;
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.function.Supplier;
 | 
			
		||||
 | 
			
		||||
@@ -45,8 +53,9 @@ import java.util.function.Supplier;
 | 
			
		||||
public class SysLoginService {
 | 
			
		||||
 | 
			
		||||
    private final SysUserMapper userMapper;
 | 
			
		||||
    private final ISysConfigService configService;
 | 
			
		||||
    private final SysPermissionService permissionService;
 | 
			
		||||
    private final CaptchaProperties captchaProperties;
 | 
			
		||||
    private final ISysPermissionService permissionService;
 | 
			
		||||
    private final ISysTenantService tenantService;
 | 
			
		||||
 | 
			
		||||
    @Value("${user.password.maxRetryCount}")
 | 
			
		||||
    private Integer maxRetryCount;
 | 
			
		||||
@@ -63,36 +72,41 @@ public class SysLoginService {
 | 
			
		||||
     * @param uuid     唯一标识
 | 
			
		||||
     * @return 结果
 | 
			
		||||
     */
 | 
			
		||||
    public String login(String username, String password, String code, String uuid) {
 | 
			
		||||
    public String login(String tenantId, String username, String password, String code, String uuid) {
 | 
			
		||||
        HttpServletRequest request = ServletUtils.getRequest();
 | 
			
		||||
        boolean captchaEnabled = configService.selectCaptchaEnabled();
 | 
			
		||||
        boolean captchaEnabled = captchaProperties.getEnable();
 | 
			
		||||
        // 验证码开关
 | 
			
		||||
        if (captchaEnabled) {
 | 
			
		||||
            validateCaptcha(username, code, uuid, request);
 | 
			
		||||
            validateCaptcha(tenantId, username, code, uuid, request);
 | 
			
		||||
        }
 | 
			
		||||
        SysUserVo user = loadUserByUsername(username);
 | 
			
		||||
        checkLogin(LoginType.PASSWORD, username, () -> !BCrypt.checkpw(password, user.getPassword()));
 | 
			
		||||
        // 校验租户
 | 
			
		||||
        checkTenant(tenantId);
 | 
			
		||||
 | 
			
		||||
        SysUserVo user = loadUserByUsername(tenantId, username);
 | 
			
		||||
        checkLogin(LoginType.PASSWORD, tenantId, username, () -> !BCrypt.checkpw(password, user.getPassword()));
 | 
			
		||||
        // 此处可根据登录用户的数据不同 自行创建 loginUser
 | 
			
		||||
        LoginUser loginUser = buildLoginUser(user);
 | 
			
		||||
        // 生成token
 | 
			
		||||
        LoginHelper.loginByDevice(loginUser, DeviceType.PC);
 | 
			
		||||
 | 
			
		||||
        recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
 | 
			
		||||
        recordLogininfor(loginUser.getTenantId(), username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
 | 
			
		||||
        recordLoginInfo(user.getUserId());
 | 
			
		||||
        return StpUtil.getTokenValue();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String smsLogin(String phonenumber, String smsCode) {
 | 
			
		||||
    public String smsLogin(String tenantId, String phonenumber, String smsCode) {
 | 
			
		||||
        // 校验租户
 | 
			
		||||
        checkTenant(tenantId);
 | 
			
		||||
        // 通过手机号查找用户
 | 
			
		||||
        SysUserVo user = loadUserByPhonenumber(phonenumber);
 | 
			
		||||
        SysUserVo user = loadUserByPhonenumber(tenantId, phonenumber);
 | 
			
		||||
 | 
			
		||||
        checkLogin(LoginType.SMS, user.getUserName(), () -> !validateSmsCode(phonenumber, smsCode));
 | 
			
		||||
        checkLogin(LoginType.SMS, tenantId, user.getUserName(), () -> !validateSmsCode(tenantId, phonenumber, smsCode));
 | 
			
		||||
        // 此处可根据登录用户的数据不同 自行创建 loginUser
 | 
			
		||||
        LoginUser loginUser = buildLoginUser(user);
 | 
			
		||||
        // 生成token
 | 
			
		||||
        LoginHelper.loginByDevice(loginUser, DeviceType.APP);
 | 
			
		||||
 | 
			
		||||
        recordLogininfor(user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
 | 
			
		||||
        recordLogininfor(loginUser.getTenantId(), user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
 | 
			
		||||
        recordLoginInfo(user.getUserId());
 | 
			
		||||
        return StpUtil.getTokenValue();
 | 
			
		||||
    }
 | 
			
		||||
@@ -104,9 +118,12 @@ public class SysLoginService {
 | 
			
		||||
        // 校验 appid + appsrcret + xcxCode 调用登录凭证校验接口 获取 session_key 与 openid
 | 
			
		||||
        String openid = "";
 | 
			
		||||
        SysUserVo user = loadUserByOpenid(openid);
 | 
			
		||||
        // 校验租户
 | 
			
		||||
        checkTenant(user.getTenantId());
 | 
			
		||||
 | 
			
		||||
        // 此处可根据登录用户的数据不同 自行创建 loginUser
 | 
			
		||||
        XcxLoginUser loginUser = new XcxLoginUser();
 | 
			
		||||
        loginUser.setTenantId(user.getTenantId());
 | 
			
		||||
        loginUser.setUserId(user.getUserId());
 | 
			
		||||
        loginUser.setUsername(user.getUserName());
 | 
			
		||||
        loginUser.setUserType(user.getUserType());
 | 
			
		||||
@@ -114,7 +131,7 @@ public class SysLoginService {
 | 
			
		||||
        // 生成token
 | 
			
		||||
        LoginHelper.loginByDevice(loginUser, DeviceType.XCX);
 | 
			
		||||
 | 
			
		||||
        recordLogininfor(user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
 | 
			
		||||
        recordLogininfor(loginUser.getTenantId(), user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
 | 
			
		||||
        recordLoginInfo(user.getUserId());
 | 
			
		||||
        return StpUtil.getTokenValue();
 | 
			
		||||
    }
 | 
			
		||||
@@ -126,7 +143,7 @@ public class SysLoginService {
 | 
			
		||||
        try {
 | 
			
		||||
            LoginUser loginUser = LoginHelper.getLoginUser();
 | 
			
		||||
            StpUtil.logout();
 | 
			
		||||
            recordLogininfor(loginUser.getUsername(), Constants.LOGOUT, MessageUtils.message("user.logout.success"));
 | 
			
		||||
            recordLogininfor(loginUser.getTenantId(), loginUser.getUsername(), Constants.LOGOUT, MessageUtils.message("user.logout.success"));
 | 
			
		||||
        } catch (NotLoginException ignored) {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -134,13 +151,15 @@ public class SysLoginService {
 | 
			
		||||
    /**
 | 
			
		||||
     * 记录登录信息
 | 
			
		||||
     *
 | 
			
		||||
     * @param tenantId 租户ID
 | 
			
		||||
     * @param username 用户名
 | 
			
		||||
     * @param status   状态
 | 
			
		||||
     * @param message  消息内容
 | 
			
		||||
     * @return
 | 
			
		||||
     */
 | 
			
		||||
    private void recordLogininfor(String username, String status, String message) {
 | 
			
		||||
    private void recordLogininfor(String tenantId, String username, String status, String message) {
 | 
			
		||||
        LogininforEvent logininforEvent = new LogininforEvent();
 | 
			
		||||
        logininforEvent.setTenantId(tenantId);
 | 
			
		||||
        logininforEvent.setUsername(username);
 | 
			
		||||
        logininforEvent.setStatus(status);
 | 
			
		||||
        logininforEvent.setMessage(message);
 | 
			
		||||
@@ -151,10 +170,10 @@ public class SysLoginService {
 | 
			
		||||
    /**
 | 
			
		||||
     * 校验短信验证码
 | 
			
		||||
     */
 | 
			
		||||
    private boolean validateSmsCode(String phonenumber, String smsCode) {
 | 
			
		||||
        String code = RedisUtils.getCacheObject(CacheConstants.CAPTCHA_CODE_KEY + phonenumber);
 | 
			
		||||
    private boolean validateSmsCode(String tenantId, String phonenumber, String smsCode) {
 | 
			
		||||
        String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + phonenumber);
 | 
			
		||||
        if (StringUtils.isBlank(code)) {
 | 
			
		||||
            recordLogininfor(phonenumber, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
 | 
			
		||||
            recordLogininfor(tenantId, phonenumber, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
 | 
			
		||||
            throw new CaptchaExpireException();
 | 
			
		||||
        }
 | 
			
		||||
        return code.equals(smsCode);
 | 
			
		||||
@@ -167,24 +186,25 @@ public class SysLoginService {
 | 
			
		||||
     * @param code     验证码
 | 
			
		||||
     * @param uuid     唯一标识
 | 
			
		||||
     */
 | 
			
		||||
    public void validateCaptcha(String username, String code, String uuid, HttpServletRequest request) {
 | 
			
		||||
        String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.defaultString(uuid, "");
 | 
			
		||||
    public void validateCaptcha(String tenantId, String username, String code, String uuid, HttpServletRequest request) {
 | 
			
		||||
        String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.defaultString(uuid, "");
 | 
			
		||||
        String captcha = RedisUtils.getCacheObject(verifyKey);
 | 
			
		||||
        RedisUtils.deleteObject(verifyKey);
 | 
			
		||||
        if (captcha == null) {
 | 
			
		||||
            recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
 | 
			
		||||
            recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
 | 
			
		||||
            throw new CaptchaExpireException();
 | 
			
		||||
        }
 | 
			
		||||
        if (!code.equalsIgnoreCase(captcha)) {
 | 
			
		||||
            recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"));
 | 
			
		||||
            recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"));
 | 
			
		||||
            throw new CaptchaException();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private SysUserVo loadUserByUsername(String username) {
 | 
			
		||||
    private SysUserVo loadUserByUsername(String tenantId, String username) {
 | 
			
		||||
        SysUser user = userMapper.selectOne(new LambdaQueryWrapper<SysUser>()
 | 
			
		||||
            .select(SysUser::getUserName, SysUser::getStatus)
 | 
			
		||||
            .eq(SysUser::getUserName, username));
 | 
			
		||||
                .select(SysUser::getUserName, SysUser::getStatus)
 | 
			
		||||
                .eq(TenantHelper.isEnable(), SysUser::getTenantId, tenantId)
 | 
			
		||||
                .eq(SysUser::getUserName, username));
 | 
			
		||||
        if (ObjectUtil.isNull(user)) {
 | 
			
		||||
            log.info("登录用户:{} 不存在.", username);
 | 
			
		||||
            throw new UserException("user.not.exists", username);
 | 
			
		||||
@@ -195,10 +215,11 @@ public class SysLoginService {
 | 
			
		||||
        return userMapper.selectUserByUserName(username);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private SysUserVo loadUserByPhonenumber(String phonenumber) {
 | 
			
		||||
    private SysUserVo loadUserByPhonenumber(String tenantId, String phonenumber) {
 | 
			
		||||
        SysUser user = userMapper.selectOne(new LambdaQueryWrapper<SysUser>()
 | 
			
		||||
            .select(SysUser::getPhonenumber, SysUser::getStatus)
 | 
			
		||||
            .eq(SysUser::getPhonenumber, phonenumber));
 | 
			
		||||
                .select(SysUser::getPhonenumber, SysUser::getStatus)
 | 
			
		||||
                .eq(TenantHelper.isEnable(), SysUser::getTenantId, tenantId)
 | 
			
		||||
                .eq(SysUser::getPhonenumber, phonenumber));
 | 
			
		||||
        if (ObjectUtil.isNull(user)) {
 | 
			
		||||
            log.info("登录用户:{} 不存在.", phonenumber);
 | 
			
		||||
            throw new UserException("user.not.exists", phonenumber);
 | 
			
		||||
@@ -228,12 +249,13 @@ public class SysLoginService {
 | 
			
		||||
     */
 | 
			
		||||
    private LoginUser buildLoginUser(SysUserVo user) {
 | 
			
		||||
        LoginUser loginUser = new LoginUser();
 | 
			
		||||
        loginUser.setTenantId(user.getTenantId());
 | 
			
		||||
        loginUser.setUserId(user.getUserId());
 | 
			
		||||
        loginUser.setDeptId(user.getDeptId());
 | 
			
		||||
        loginUser.setUsername(user.getUserName());
 | 
			
		||||
        loginUser.setUserType(user.getUserType());
 | 
			
		||||
        loginUser.setMenuPermission(permissionService.getMenuPermission(user.getUserId(), user.isAdmin()));
 | 
			
		||||
        loginUser.setRolePermission(permissionService.getRolePermission(user.getUserId(), user.isAdmin()));
 | 
			
		||||
        loginUser.setMenuPermission(permissionService.getMenuPermission(user.getUserId()));
 | 
			
		||||
        loginUser.setRolePermission(permissionService.getRolePermission(user.getUserId()));
 | 
			
		||||
        loginUser.setDeptName(ObjectUtil.isNull(user.getDept()) ? "" : user.getDept().getDeptName());
 | 
			
		||||
        List<RoleDTO> roles = BeanUtil.copyToList(user.getRoles(), RoleDTO.class);
 | 
			
		||||
        loginUser.setRoles(roles);
 | 
			
		||||
@@ -257,15 +279,15 @@ public class SysLoginService {
 | 
			
		||||
    /**
 | 
			
		||||
     * 登录校验
 | 
			
		||||
     */
 | 
			
		||||
    private void checkLogin(LoginType loginType, String username, Supplier<Boolean> supplier) {
 | 
			
		||||
        String errorKey = CacheConstants.PWD_ERR_CNT_KEY + username;
 | 
			
		||||
    private void checkLogin(LoginType loginType, String tenantId, String username, Supplier<Boolean> supplier) {
 | 
			
		||||
        String errorKey = GlobalConstants.PWD_ERR_CNT_KEY + username;
 | 
			
		||||
        String loginFail = Constants.LOGIN_FAIL;
 | 
			
		||||
 | 
			
		||||
        // 获取用户登录错误次数(可自定义限制策略 例如: key + username + ip)
 | 
			
		||||
        Integer errorNumber = RedisUtils.getCacheObject(errorKey);
 | 
			
		||||
        // 锁定时间内登录 则踢出
 | 
			
		||||
        if (ObjectUtil.isNotNull(errorNumber) && errorNumber.equals(maxRetryCount)) {
 | 
			
		||||
            recordLogininfor(username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime));
 | 
			
		||||
            recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime));
 | 
			
		||||
            throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -275,12 +297,12 @@ public class SysLoginService {
 | 
			
		||||
            // 达到规定错误次数 则锁定登录
 | 
			
		||||
            if (errorNumber.equals(maxRetryCount)) {
 | 
			
		||||
                RedisUtils.setCacheObject(errorKey, errorNumber, Duration.ofMinutes(lockTime));
 | 
			
		||||
                recordLogininfor(username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime));
 | 
			
		||||
                recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime));
 | 
			
		||||
                throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime);
 | 
			
		||||
            } else {
 | 
			
		||||
                // 未达到规定错误次数 则递增
 | 
			
		||||
                RedisUtils.setCacheObject(errorKey, errorNumber);
 | 
			
		||||
                recordLogininfor(username, loginFail, MessageUtils.message(loginType.getRetryLimitCount(), errorNumber));
 | 
			
		||||
                recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitCount(), errorNumber));
 | 
			
		||||
                throw new UserException(loginType.getRetryLimitCount(), errorNumber);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -288,4 +310,23 @@ public class SysLoginService {
 | 
			
		||||
        // 登录成功 清空错误次数
 | 
			
		||||
        RedisUtils.deleteObject(errorKey);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void checkTenant(String tenantId) {
 | 
			
		||||
        if (!TenantHelper.isEnable()) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        SysTenantVo tenant = tenantService.queryByTenantId(tenantId);
 | 
			
		||||
        if (ObjectUtil.isNull(tenant)) {
 | 
			
		||||
            log.info("登录租户:{} 不存在.", tenantId);
 | 
			
		||||
            throw new TenantException("tenant.not.exists");
 | 
			
		||||
        } else if (TenantStatus.DISABLE.getCode().equals(tenant.getStatus())) {
 | 
			
		||||
            log.info("登录租户:{} 已被停用.", tenantId);
 | 
			
		||||
            throw new TenantException("tenant.blocked");
 | 
			
		||||
        } else if (ObjectUtil.isNotNull(tenant.getExpireTime())
 | 
			
		||||
                && new Date().after(tenant.getExpireTime())) {
 | 
			
		||||
            log.info("登录租户:{} 已超过有效期.", tenantId);
 | 
			
		||||
            throw new TenantException("tenant.expired");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
package com.ruoyi.system.service;
 | 
			
		||||
package com.ruoyi.web.service;
 | 
			
		||||
 | 
			
		||||
import cn.dev33.satoken.secure.BCrypt;
 | 
			
		||||
import com.ruoyi.common.core.constant.CacheConstants;
 | 
			
		||||
import com.ruoyi.common.core.constant.Constants;
 | 
			
		||||
import com.ruoyi.common.core.constant.GlobalConstants;
 | 
			
		||||
import com.ruoyi.common.core.constant.UserConstants;
 | 
			
		||||
import com.ruoyi.common.core.domain.model.RegisterBody;
 | 
			
		||||
import com.ruoyi.common.core.enums.UserType;
 | 
			
		||||
@@ -15,7 +15,9 @@ import com.ruoyi.common.core.utils.SpringUtils;
 | 
			
		||||
import com.ruoyi.common.core.utils.StringUtils;
 | 
			
		||||
import com.ruoyi.common.log.event.LogininforEvent;
 | 
			
		||||
import com.ruoyi.common.redis.utils.RedisUtils;
 | 
			
		||||
import com.ruoyi.common.web.config.properties.CaptchaProperties;
 | 
			
		||||
import com.ruoyi.system.domain.bo.SysUserBo;
 | 
			
		||||
import com.ruoyi.system.service.ISysUserService;
 | 
			
		||||
import jakarta.servlet.http.HttpServletRequest;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import org.springframework.stereotype.Service;
 | 
			
		||||
@@ -30,22 +32,23 @@ import org.springframework.stereotype.Service;
 | 
			
		||||
public class SysRegisterService {
 | 
			
		||||
 | 
			
		||||
    private final ISysUserService userService;
 | 
			
		||||
    private final ISysConfigService configService;
 | 
			
		||||
    private final CaptchaProperties captchaProperties;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 注册
 | 
			
		||||
     */
 | 
			
		||||
    public void register(RegisterBody registerBody) {
 | 
			
		||||
        HttpServletRequest request = ServletUtils.getRequest();
 | 
			
		||||
        String tenantId = registerBody.getTenantId();
 | 
			
		||||
        String username = registerBody.getUsername();
 | 
			
		||||
        String password = registerBody.getPassword();
 | 
			
		||||
        // 校验用户类型是否存在
 | 
			
		||||
        String userType = UserType.getUserType(registerBody.getUserType()).getUserType();
 | 
			
		||||
 | 
			
		||||
        boolean captchaEnabled = configService.selectCaptchaEnabled();
 | 
			
		||||
        boolean captchaEnabled = captchaProperties.getEnable();
 | 
			
		||||
        // 验证码开关
 | 
			
		||||
        if (captchaEnabled) {
 | 
			
		||||
            validateCaptcha(username, registerBody.getCode(), registerBody.getUuid(), request);
 | 
			
		||||
            validateCaptcha(tenantId, username, registerBody.getCode(), registerBody.getUuid(), request);
 | 
			
		||||
        }
 | 
			
		||||
        SysUserBo sysUser = new SysUserBo();
 | 
			
		||||
        sysUser.setUserName(username);
 | 
			
		||||
@@ -56,11 +59,11 @@ public class SysRegisterService {
 | 
			
		||||
        if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(sysUser))) {
 | 
			
		||||
            throw new UserException("user.register.save.error", username);
 | 
			
		||||
        }
 | 
			
		||||
        boolean regFlag = userService.registerUser(sysUser);
 | 
			
		||||
        boolean regFlag = userService.registerUser(sysUser, tenantId);
 | 
			
		||||
        if (!regFlag) {
 | 
			
		||||
            throw new UserException("user.register.error");
 | 
			
		||||
        }
 | 
			
		||||
        recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.register.success"));
 | 
			
		||||
        recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.register.success"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -71,16 +74,16 @@ public class SysRegisterService {
 | 
			
		||||
     * @param uuid     唯一标识
 | 
			
		||||
     * @return 结果
 | 
			
		||||
     */
 | 
			
		||||
    public void validateCaptcha(String username, String code, String uuid, HttpServletRequest request) {
 | 
			
		||||
        String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.defaultString(uuid, "");
 | 
			
		||||
    public void validateCaptcha(String tenantId, String username, String code, String uuid, HttpServletRequest request) {
 | 
			
		||||
        String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.defaultString(uuid, "");
 | 
			
		||||
        String captcha = RedisUtils.getCacheObject(verifyKey);
 | 
			
		||||
        RedisUtils.deleteObject(verifyKey);
 | 
			
		||||
        if (captcha == null) {
 | 
			
		||||
            recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.jcaptcha.expire"));
 | 
			
		||||
            recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.jcaptcha.expire"));
 | 
			
		||||
            throw new CaptchaExpireException();
 | 
			
		||||
        }
 | 
			
		||||
        if (!code.equalsIgnoreCase(captcha)) {
 | 
			
		||||
            recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.jcaptcha.error"));
 | 
			
		||||
            recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.jcaptcha.error"));
 | 
			
		||||
            throw new CaptchaException();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -88,13 +91,15 @@ public class SysRegisterService {
 | 
			
		||||
    /**
 | 
			
		||||
     * 记录登录信息
 | 
			
		||||
     *
 | 
			
		||||
     * @param tenantId 租户ID
 | 
			
		||||
     * @param username 用户名
 | 
			
		||||
     * @param status   状态
 | 
			
		||||
     * @param message  消息内容
 | 
			
		||||
     * @return
 | 
			
		||||
     */
 | 
			
		||||
    private void recordLogininfor(String username, String status, String message) {
 | 
			
		||||
    private void recordLogininfor(String tenantId, String username, String status, String message) {
 | 
			
		||||
        LogininforEvent logininforEvent = new LogininforEvent();
 | 
			
		||||
        logininforEvent.setTenantId(tenantId);
 | 
			
		||||
        logininforEvent.setUsername(username);
 | 
			
		||||
        logininforEvent.setStatus(status);
 | 
			
		||||
        logininforEvent.setMessage(message);
 | 
			
		||||
@@ -92,9 +92,11 @@ spring:
 | 
			
		||||
        # 空闲连接存活最大时间,默认10分钟
 | 
			
		||||
        idleTimeout: 600000
 | 
			
		||||
        # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟
 | 
			
		||||
        maxLifetime: 900000
 | 
			
		||||
        maxLifetime: 1800000
 | 
			
		||||
        # 连接测试query(配置检测连接是否有效)
 | 
			
		||||
        connectionTestQuery: SELECT 1
 | 
			
		||||
        # 多久检查一次连接的活性
 | 
			
		||||
        keepaliveTime: 30000
 | 
			
		||||
 | 
			
		||||
--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉)
 | 
			
		||||
spring.data:
 | 
			
		||||
 
 | 
			
		||||
@@ -95,9 +95,11 @@ spring:
 | 
			
		||||
        # 空闲连接存活最大时间,默认10分钟
 | 
			
		||||
        idleTimeout: 600000
 | 
			
		||||
        # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟
 | 
			
		||||
        maxLifetime: 900000
 | 
			
		||||
        maxLifetime: 1800000
 | 
			
		||||
        # 连接测试query(配置检测连接是否有效)
 | 
			
		||||
        connectionTestQuery: SELECT 1
 | 
			
		||||
        # 多久检查一次连接的活性
 | 
			
		||||
        keepaliveTime: 30000
 | 
			
		||||
 | 
			
		||||
--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉)
 | 
			
		||||
spring.data:
 | 
			
		||||
 
 | 
			
		||||
@@ -10,10 +10,9 @@ ruoyi:
 | 
			
		||||
  demoEnabled: true
 | 
			
		||||
  # 获取ip地址开关
 | 
			
		||||
  addressEnabled: true
 | 
			
		||||
  # 缓存懒加载
 | 
			
		||||
  cacheLazy: false
 | 
			
		||||
 | 
			
		||||
captcha:
 | 
			
		||||
  enable: true
 | 
			
		||||
  # 页面 <参数设置> 可开启关闭 验证码校验
 | 
			
		||||
  # 验证码类型 math 数组计算 char 字符验证
 | 
			
		||||
  type: MATH
 | 
			
		||||
@@ -136,6 +135,20 @@ security:
 | 
			
		||||
    - /actuator
 | 
			
		||||
    - /actuator/**
 | 
			
		||||
 | 
			
		||||
# 多租户配置
 | 
			
		||||
tenant:
 | 
			
		||||
  # 是否开启
 | 
			
		||||
  enable: true
 | 
			
		||||
  # 排除表
 | 
			
		||||
  excludes:
 | 
			
		||||
    - sys_menu
 | 
			
		||||
    - sys_tenant
 | 
			
		||||
    - sys_tenant_package
 | 
			
		||||
    - sys_role_dept
 | 
			
		||||
    - sys_role_menu
 | 
			
		||||
    - sys_user_post
 | 
			
		||||
    - sys_user_role
 | 
			
		||||
 | 
			
		||||
# MyBatisPlus配置
 | 
			
		||||
# https://baomidou.com/config/
 | 
			
		||||
mybatis-plus:
 | 
			
		||||
 
 | 
			
		||||
@@ -43,3 +43,8 @@ sms.code.not.blank=短信验证码不能为空
 | 
			
		||||
sms.code.retry.limit.count=短信验证码输入错误{0}次
 | 
			
		||||
sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}分钟
 | 
			
		||||
xcx.code.not.blank=小程序code不能为空
 | 
			
		||||
##租户
 | 
			
		||||
tenant.number.not.blank=租户编号不能为空
 | 
			
		||||
tenant.not.exists=对不起, 您的租户不存在,请联系管理员
 | 
			
		||||
tenant.blocked=对不起,您的租户已禁用,请联系管理员
 | 
			
		||||
tenant.expired=对不起,您的租户已过期,请联系管理员
 | 
			
		||||
 
 | 
			
		||||
@@ -43,3 +43,8 @@ sms.code.not.blank=Sms code cannot be blank
 | 
			
		||||
sms.code.retry.limit.count=Sms code input error {0} times
 | 
			
		||||
sms.code.retry.limit.exceed=Sms code input error {0} times, account locked for {1} minutes
 | 
			
		||||
xcx.code.not.blank=Mini program code cannot be blank
 | 
			
		||||
##租户
 | 
			
		||||
tenant.number.not.blank=Tenant number cannot be blank
 | 
			
		||||
tenant.not.exists=Sorry, your tenant does not exist. Please contact the administrator
 | 
			
		||||
tenant.blocked=Sorry, your tenant is disabled. Please contact the administrator
 | 
			
		||||
tenant.expired=Sorry, your tenant has expired. Please contact the administrator.
 | 
			
		||||
 
 | 
			
		||||
@@ -43,3 +43,8 @@ sms.code.not.blank=短信验证码不能为空
 | 
			
		||||
sms.code.retry.limit.count=短信验证码输入错误{0}次
 | 
			
		||||
sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}分钟
 | 
			
		||||
xcx.code.not.blank=小程序code不能为空
 | 
			
		||||
##租户
 | 
			
		||||
tenant.number.not.blank=租户编号不能为空
 | 
			
		||||
tenant.not.exists=对不起, 您的租户不存在,请联系管理员
 | 
			
		||||
tenant.blocked=对不起,您的租户已禁用,请联系管理员
 | 
			
		||||
tenant.expired=对不起,您的租户已过期,请联系管理员
 | 
			
		||||
 
 | 
			
		||||
@@ -31,6 +31,7 @@
 | 
			
		||||
        <module>ruoyi-common-sensitive</module>
 | 
			
		||||
        <module>ruoyi-common-json</module>
 | 
			
		||||
        <module>ruoyi-common-encrypt</module>
 | 
			
		||||
        <module>ruoyi-common-tenant</module>
 | 
			
		||||
    </modules>
 | 
			
		||||
 | 
			
		||||
    <artifactId>ruoyi-common</artifactId>
 | 
			
		||||
 
 | 
			
		||||
@@ -152,6 +152,13 @@
 | 
			
		||||
                <version>${revision}</version>
 | 
			
		||||
            </dependency>
 | 
			
		||||
 | 
			
		||||
            <!-- 租户模块 -->
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>com.ruoyi</groupId>
 | 
			
		||||
                <artifactId>ruoyi-common-tenant</artifactId>
 | 
			
		||||
                <version>${revision}</version>
 | 
			
		||||
            </dependency>
 | 
			
		||||
 | 
			
		||||
        </dependencies>
 | 
			
		||||
    </dependencyManagement>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -36,11 +36,6 @@ public class RuoYiConfig {
 | 
			
		||||
     */
 | 
			
		||||
    private boolean demoEnabled;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 缓存懒加载
 | 
			
		||||
     */
 | 
			
		||||
    private boolean cacheLazy;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取地址开关
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
@@ -3,25 +3,15 @@ package com.ruoyi.common.core.constant;
 | 
			
		||||
/**
 | 
			
		||||
 * 缓存的key 常量
 | 
			
		||||
 *
 | 
			
		||||
 * @author ruoyi
 | 
			
		||||
 * @author Lion Li
 | 
			
		||||
 */
 | 
			
		||||
public interface CacheConstants {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 登录用户 redis key
 | 
			
		||||
     */
 | 
			
		||||
    String LOGIN_TOKEN_KEY = "Authorization:login:token:";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 在线用户 redis key
 | 
			
		||||
     */
 | 
			
		||||
    String ONLINE_TOKEN_KEY = "online_tokens:";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 验证码 redis key
 | 
			
		||||
     */
 | 
			
		||||
    String CAPTCHA_CODE_KEY = "captcha_codes:";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 参数管理 cache key
 | 
			
		||||
     */
 | 
			
		||||
@@ -32,18 +22,4 @@ public interface CacheConstants {
 | 
			
		||||
     */
 | 
			
		||||
    String SYS_DICT_KEY = "sys_dict:";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 防重提交 redis key
 | 
			
		||||
     */
 | 
			
		||||
    String REPEAT_SUBMIT_KEY = "repeat_submit:";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 限流 redis key
 | 
			
		||||
     */
 | 
			
		||||
    String RATE_LIMIT_KEY = "rate_limit:";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 登录账户密码错误次数 redis key
 | 
			
		||||
     */
 | 
			
		||||
    String PWD_ERR_CNT_KEY = "pwd_err_cnt:";
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,11 @@ public interface CacheNames {
 | 
			
		||||
     */
 | 
			
		||||
    String SYS_DICT = "sys_dict";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 租户
 | 
			
		||||
     */
 | 
			
		||||
    String SYS_TENANT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_tenant#30d";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 用户账户
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
@@ -72,5 +72,10 @@ public interface Constants {
 | 
			
		||||
     */
 | 
			
		||||
    String TOKEN = "token";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 顶级部门id
 | 
			
		||||
     */
 | 
			
		||||
    Long TOP_PARENT_ID = 0L;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,39 @@
 | 
			
		||||
package com.ruoyi.common.core.constant;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 全局的key常量 (业务无关的key)
 | 
			
		||||
 *
 | 
			
		||||
 * @author Lion Li
 | 
			
		||||
 */
 | 
			
		||||
public interface GlobalConstants {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 全局 redis key (业务无关的key)
 | 
			
		||||
     */
 | 
			
		||||
    String GLOBAL_REDIS_KEY = "global:";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 登录用户 redis key
 | 
			
		||||
     */
 | 
			
		||||
    String LOGIN_TOKEN_KEY = GLOBAL_REDIS_KEY + "Authorization:login:token:";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 验证码 redis key
 | 
			
		||||
     */
 | 
			
		||||
    String CAPTCHA_CODE_KEY = GLOBAL_REDIS_KEY + "captcha_codes:";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 防重提交 redis key
 | 
			
		||||
     */
 | 
			
		||||
    String REPEAT_SUBMIT_KEY = GLOBAL_REDIS_KEY + "repeat_submit:";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 限流 redis key
 | 
			
		||||
     */
 | 
			
		||||
    String RATE_LIMIT_KEY = GLOBAL_REDIS_KEY + "rate_limit:";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 登录账户密码错误次数 redis key
 | 
			
		||||
     */
 | 
			
		||||
    String PWD_ERR_CNT_KEY = GLOBAL_REDIS_KEY + "pwd_err_cnt:";
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,51 @@
 | 
			
		||||
package com.ruoyi.common.core.constant;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 租户常量信息
 | 
			
		||||
 *
 | 
			
		||||
 * @author Lion Li
 | 
			
		||||
 */
 | 
			
		||||
public interface TenantConstants {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 租户正常状态
 | 
			
		||||
     */
 | 
			
		||||
    String NORMAL = "0";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 租户封禁状态
 | 
			
		||||
     */
 | 
			
		||||
    String DISABLE = "1";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 校验返回结果码
 | 
			
		||||
     */
 | 
			
		||||
    String PASS = "0";
 | 
			
		||||
    String NOT_PASS = "1";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 超级管理员ID
 | 
			
		||||
     */
 | 
			
		||||
    Long SUPER_ADMIN_ID = 1L;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 超级管理员角色 roleKey
 | 
			
		||||
     */
 | 
			
		||||
    String SUPER_ADMIN_ROLE_KEY = "superadmin";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 租户管理员角色 roleKey
 | 
			
		||||
     */
 | 
			
		||||
    String TENANT_ADMIN_ROLE_KEY = "admin";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 租户管理员角色名称
 | 
			
		||||
     */
 | 
			
		||||
    String TENANT_ADMIN_ROLE_NAME = "管理员";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 默认租户ID
 | 
			
		||||
     */
 | 
			
		||||
    String DEFAULT_TENANT_ID = "000000";
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -15,6 +15,12 @@ import jakarta.validation.constraints.NotBlank;
 | 
			
		||||
@Data
 | 
			
		||||
public class LoginBody {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 租户ID
 | 
			
		||||
     */
 | 
			
		||||
    @NotBlank(message = "{tenant.number.not.blank}")
 | 
			
		||||
    private String tenantId;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 用户名
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,11 @@ public class LoginUser implements Serializable {
 | 
			
		||||
    @Serial
 | 
			
		||||
    private static final long serialVersionUID = 1L;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 租户ID
 | 
			
		||||
     */
 | 
			
		||||
    private String tenantId;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 用户ID
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,12 @@ import jakarta.validation.constraints.NotBlank;
 | 
			
		||||
@Data
 | 
			
		||||
public class SmsLoginBody {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 租户ID
 | 
			
		||||
     */
 | 
			
		||||
    @NotBlank(message = "{tenant.number.not.blank}")
 | 
			
		||||
    private String tenantId;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 用户名
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,30 @@
 | 
			
		||||
package com.ruoyi.common.core.enums;
 | 
			
		||||
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 用户状态
 | 
			
		||||
 *
 | 
			
		||||
 * @author LionLi
 | 
			
		||||
 */
 | 
			
		||||
@Getter
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
public enum TenantStatus {
 | 
			
		||||
    /**
 | 
			
		||||
     * 正常
 | 
			
		||||
     */
 | 
			
		||||
    OK("0", "正常"),
 | 
			
		||||
    /**
 | 
			
		||||
     * 停用
 | 
			
		||||
     */
 | 
			
		||||
    DISABLE("1", "停用"),
 | 
			
		||||
    /**
 | 
			
		||||
     * 删除
 | 
			
		||||
     */
 | 
			
		||||
    DELETED("2", "删除");
 | 
			
		||||
 | 
			
		||||
    private final String code;
 | 
			
		||||
    private final String info;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -92,7 +92,7 @@ public class ServletUtils extends JakartaServletUtil {
 | 
			
		||||
    public static Map<String, String> getParamMap(ServletRequest request) {
 | 
			
		||||
        Map<String, String> params = new HashMap<>();
 | 
			
		||||
        for (Map.Entry<String, String[]> entry : getParams(request).entrySet()) {
 | 
			
		||||
            params.put(entry.getKey(), StringUtils.join(entry.getValue(), ","));
 | 
			
		||||
            params.put(entry.getKey(), StringUtils.join(entry.getValue(), StringUtils.SEPARATOR));
 | 
			
		||||
        }
 | 
			
		||||
        return params;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -42,7 +42,7 @@ public class StreamUtils {
 | 
			
		||||
     * @return 拼接后的list
 | 
			
		||||
     */
 | 
			
		||||
    public static <E> String join(Collection<E> collection, Function<E, String> function) {
 | 
			
		||||
        return join(collection, function, ",");
 | 
			
		||||
        return join(collection, function, StringUtils.SEPARATOR);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,16 @@
 | 
			
		||||
package com.ruoyi.common.core.utils;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.collection.CollUtil;
 | 
			
		||||
import cn.hutool.core.convert.Convert;
 | 
			
		||||
import cn.hutool.core.lang.Validator;
 | 
			
		||||
import cn.hutool.core.util.StrUtil;
 | 
			
		||||
import lombok.AccessLevel;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
import org.springframework.util.AntPathMatcher;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 字符串工具类
 | 
			
		||||
@@ -20,6 +20,8 @@ import java.util.Set;
 | 
			
		||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
 | 
			
		||||
public class StringUtils extends org.apache.commons.lang3.StringUtils {
 | 
			
		||||
 | 
			
		||||
    public static final String SEPARATOR = ",";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取参数不为空值
 | 
			
		||||
     *
 | 
			
		||||
@@ -233,7 +235,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
 | 
			
		||||
    /**
 | 
			
		||||
     * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。
 | 
			
		||||
     *
 | 
			
		||||
     * @param num 数字对象
 | 
			
		||||
     * @param num  数字对象
 | 
			
		||||
     * @param size 字符串指定长度
 | 
			
		||||
     * @return 返回数字的字符串格式,该字符串为指定长度。
 | 
			
		||||
     */
 | 
			
		||||
@@ -244,9 +246,9 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
 | 
			
		||||
    /**
 | 
			
		||||
     * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。
 | 
			
		||||
     *
 | 
			
		||||
     * @param s 原始字符串
 | 
			
		||||
     * @param s    原始字符串
 | 
			
		||||
     * @param size 字符串指定长度
 | 
			
		||||
     * @param c 用于补齐的字符
 | 
			
		||||
     * @param c    用于补齐的字符
 | 
			
		||||
     * @return 返回指定长度的字符串,由原字符串左补齐或截取得到。
 | 
			
		||||
     */
 | 
			
		||||
    public static String padl(final String s, final int size, final char c) {
 | 
			
		||||
@@ -265,4 +267,55 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
 | 
			
		||||
        return sb.toString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 切分字符串(分隔符默认逗号)
 | 
			
		||||
     *
 | 
			
		||||
     * @param str 被切分的字符串
 | 
			
		||||
     * @return 分割后的数据列表
 | 
			
		||||
     */
 | 
			
		||||
    public static List<String> splitList(String str) {
 | 
			
		||||
        return splitTo(str, Convert::toStr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 切分字符串
 | 
			
		||||
     *
 | 
			
		||||
     * @param str       被切分的字符串
 | 
			
		||||
     * @param separator 分隔符
 | 
			
		||||
     * @return 分割后的数据列表
 | 
			
		||||
     */
 | 
			
		||||
    public static List<String> splitList(String str, String separator) {
 | 
			
		||||
        return splitTo(str, separator, Convert::toStr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 切分字符串自定义转换(分隔符默认逗号)
 | 
			
		||||
     *
 | 
			
		||||
     * @param str    被切分的字符串
 | 
			
		||||
     * @param mapper 自定义转换
 | 
			
		||||
     * @return 分割后的数据列表
 | 
			
		||||
     */
 | 
			
		||||
    public static <T> List<T> splitTo(String str, Function<? super Object, T> mapper) {
 | 
			
		||||
        return splitTo(str, SEPARATOR, mapper);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 切分字符串自定义转换
 | 
			
		||||
     *
 | 
			
		||||
     * @param str       被切分的字符串
 | 
			
		||||
     * @param separator 分隔符
 | 
			
		||||
     * @param mapper    自定义转换
 | 
			
		||||
     * @return 分割后的数据列表
 | 
			
		||||
     */
 | 
			
		||||
    public static <T> List<T> splitTo(String str, String separator, Function<? super Object, T> mapper) {
 | 
			
		||||
        if (isBlank(str)) {
 | 
			
		||||
            return new ArrayList<>(0);
 | 
			
		||||
        }
 | 
			
		||||
        return StrUtil.split(str, separator)
 | 
			
		||||
            .stream()
 | 
			
		||||
            .filter(Objects::nonNull)
 | 
			
		||||
            .map(mapper)
 | 
			
		||||
            .collect(Collectors.toList());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
package com.ruoyi.common.excel.annotation;
 | 
			
		||||
 | 
			
		||||
import com.ruoyi.common.core.utils.StringUtils;
 | 
			
		||||
 | 
			
		||||
import java.lang.annotation.*;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -25,6 +27,6 @@ public @interface ExcelDictFormat {
 | 
			
		||||
    /**
 | 
			
		||||
     * 分隔符,读取字符串组内容
 | 
			
		||||
     */
 | 
			
		||||
    String separator() default ",";
 | 
			
		||||
    String separator() default StringUtils.SEPARATOR;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -269,7 +269,7 @@ public class ExcelUtil {
 | 
			
		||||
     */
 | 
			
		||||
    public static String convertByExp(String propertyValue, String converterExp, String separator) {
 | 
			
		||||
        StringBuilder propertyString = new StringBuilder();
 | 
			
		||||
        String[] convertSource = converterExp.split(",");
 | 
			
		||||
        String[] convertSource = converterExp.split(StringUtils.SEPARATOR);
 | 
			
		||||
        for (String item : convertSource) {
 | 
			
		||||
            String[] itemArray = item.split("=");
 | 
			
		||||
            if (StringUtils.containsAny(propertyValue, separator)) {
 | 
			
		||||
@@ -298,7 +298,7 @@ public class ExcelUtil {
 | 
			
		||||
     */
 | 
			
		||||
    public static String reverseByExp(String propertyValue, String converterExp, String separator) {
 | 
			
		||||
        StringBuilder propertyString = new StringBuilder();
 | 
			
		||||
        String[] convertSource = converterExp.split(",");
 | 
			
		||||
        String[] convertSource = converterExp.split(StringUtils.SEPARATOR);
 | 
			
		||||
        for (String item : convertSource) {
 | 
			
		||||
            String[] itemArray = item.split("=");
 | 
			
		||||
            if (StringUtils.containsAny(propertyValue, separator)) {
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@ package com.ruoyi.common.idempotent.aspectj;
 | 
			
		||||
import cn.dev33.satoken.SaManager;
 | 
			
		||||
import cn.hutool.core.util.ObjectUtil;
 | 
			
		||||
import cn.hutool.crypto.SecureUtil;
 | 
			
		||||
import com.ruoyi.common.core.constant.CacheConstants;
 | 
			
		||||
import com.ruoyi.common.core.constant.GlobalConstants;
 | 
			
		||||
import com.ruoyi.common.core.domain.R;
 | 
			
		||||
import com.ruoyi.common.core.exception.ServiceException;
 | 
			
		||||
import com.ruoyi.common.core.utils.MessageUtils;
 | 
			
		||||
@@ -57,7 +57,7 @@ public class RepeatSubmitAspect {
 | 
			
		||||
 | 
			
		||||
        submitKey = SecureUtil.md5(submitKey + ":" + nowParams);
 | 
			
		||||
        // 唯一标识(指定key + url + 消息头)
 | 
			
		||||
        String cacheRepeatKey = CacheConstants.REPEAT_SUBMIT_KEY + url + submitKey;
 | 
			
		||||
        String cacheRepeatKey = GlobalConstants.REPEAT_SUBMIT_KEY + url + submitKey;
 | 
			
		||||
        String key = RedisUtils.getCacheObject(cacheRepeatKey);
 | 
			
		||||
        if (key == null) {
 | 
			
		||||
            RedisUtils.setCacheObject(cacheRepeatKey, "", Duration.ofMillis(interval));
 | 
			
		||||
 
 | 
			
		||||
@@ -67,6 +67,7 @@ public class LogAspect {
 | 
			
		||||
 | 
			
		||||
            // *========数据库日志=========*//
 | 
			
		||||
            OperLogEvent operLog = new OperLogEvent();
 | 
			
		||||
            operLog.setTenantId(LoginHelper.getTenantId());
 | 
			
		||||
            operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
 | 
			
		||||
            // 请求的地址
 | 
			
		||||
            String ip = ServletUtils.getClientIP();
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,11 @@ public class LogininforEvent implements Serializable {
 | 
			
		||||
    @Serial
 | 
			
		||||
    private static final long serialVersionUID = 1L;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 租户ID
 | 
			
		||||
     */
 | 
			
		||||
    private String tenantId;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 用户账号
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,11 @@ public class OperLogEvent implements Serializable {
 | 
			
		||||
     */
 | 
			
		||||
    private Long operId;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 租户ID
 | 
			
		||||
     */
 | 
			
		||||
    private String tenantId;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 操作模块
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
@@ -1,41 +0,0 @@
 | 
			
		||||
package com.ruoyi.common.mybatis.core.domain;
 | 
			
		||||
 | 
			
		||||
import com.baomidou.mybatisplus.annotation.TableField;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.EqualsAndHashCode;
 | 
			
		||||
 | 
			
		||||
import java.io.Serial;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tree基类
 | 
			
		||||
 *
 | 
			
		||||
 * @author Lion Li
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
@Data
 | 
			
		||||
@EqualsAndHashCode(callSuper = true)
 | 
			
		||||
public class TreeEntity<T> extends BaseEntity {
 | 
			
		||||
 | 
			
		||||
    @Serial
 | 
			
		||||
    private static final long serialVersionUID = 1L;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 父菜单名称
 | 
			
		||||
     */
 | 
			
		||||
    @TableField(exist = false)
 | 
			
		||||
    private String parentName;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 父菜单ID
 | 
			
		||||
     */
 | 
			
		||||
    private Long parentId;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 子部门
 | 
			
		||||
     */
 | 
			
		||||
    @TableField(exist = false)
 | 
			
		||||
    private List<T> children = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -6,7 +6,7 @@ import com.baomidou.mybatisplus.core.conditions.Wrapper;
 | 
			
		||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 | 
			
		||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 | 
			
		||||
import com.baomidou.mybatisplus.core.metadata.IPage;
 | 
			
		||||
import com.baomidou.mybatisplus.core.toolkit.*;
 | 
			
		||||
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
 | 
			
		||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 | 
			
		||||
import com.baomidou.mybatisplus.extension.toolkit.Db;
 | 
			
		||||
import com.ruoyi.common.core.utils.BeanCopyUtils;
 | 
			
		||||
@@ -17,6 +17,9 @@ import java.io.Serializable;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 自定义 Mapper 接口, 实现 自定义扩展
 | 
			
		||||
@@ -193,4 +196,8 @@ public interface BaseMapperPlus<M, T, V> extends BaseMapper<T> {
 | 
			
		||||
        return (P) voPage;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    default <C> List<C> selectObjs(Wrapper<T> wrapper, Function<? super Object, C> mapper) {
 | 
			
		||||
        return this.selectObjs(wrapper).stream().filter(Objects::nonNull).map(mapper).collect(Collectors.toList());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -89,8 +89,8 @@ public class PageQuery implements Serializable {
 | 
			
		||||
        // 兼容前端排序类型
 | 
			
		||||
        isAsc = StringUtils.replaceEach(isAsc, new String[]{"ascending", "descending"}, new String[]{"asc", "desc"});
 | 
			
		||||
 | 
			
		||||
        String[] orderByArr = orderBy.split(",");
 | 
			
		||||
        String[] isAscArr = isAsc.split(",");
 | 
			
		||||
        String[] orderByArr = orderBy.split(StringUtils.SEPARATOR);
 | 
			
		||||
        String[] isAscArr = isAsc.split(StringUtils.SEPARATOR);
 | 
			
		||||
        if (isAscArr.length != 1 && isAscArr.length != orderByArr.length) {
 | 
			
		||||
            throw new ServiceException("排序参数有误");
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
package com.ruoyi.common.mybatis.helper;
 | 
			
		||||
package com.ruoyi.common.mybatis.handler;
 | 
			
		||||
 | 
			
		||||
import com.ruoyi.common.core.domain.R;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
@@ -35,7 +35,6 @@ import java.lang.reflect.Method;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
import java.util.concurrent.ConcurrentHashMap;
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 数据权限过滤
 | 
			
		||||
@@ -79,7 +78,7 @@ public class PlusDataPermissionHandler {
 | 
			
		||||
            DataPermissionHelper.setVariable("user", currentUser);
 | 
			
		||||
        }
 | 
			
		||||
        // 如果是超级管理员,则不过滤数据
 | 
			
		||||
        if (LoginHelper.isAdmin()) {
 | 
			
		||||
        if (LoginHelper.isSuperAdmin()) {
 | 
			
		||||
            return where;
 | 
			
		||||
        }
 | 
			
		||||
        String dataFilterSql = buildDataFilter(dataColumns, isSelect);
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,8 @@ package com.ruoyi.common.mybatis.helper;
 | 
			
		||||
import cn.dev33.satoken.context.SaHolder;
 | 
			
		||||
import cn.dev33.satoken.context.model.SaStorage;
 | 
			
		||||
import cn.hutool.core.util.ObjectUtil;
 | 
			
		||||
import com.baomidou.mybatisplus.core.plugins.IgnoreStrategy;
 | 
			
		||||
import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
 | 
			
		||||
import lombok.AccessLevel;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
 | 
			
		||||
@@ -44,4 +46,19 @@ public class DataPermissionHelper {
 | 
			
		||||
        }
 | 
			
		||||
        throw new NullPointerException("data permission context type exception");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 开启忽略数据权限(开启后需手动调用 {@link #disableIgnore()} 关闭)
 | 
			
		||||
     */
 | 
			
		||||
    public static void enableIgnore() {
 | 
			
		||||
        InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().dataPermission(true).build());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 关闭忽略数据权限
 | 
			
		||||
     */
 | 
			
		||||
    public static void disableIgnore() {
 | 
			
		||||
        InterceptorIgnoreHelper.clearIgnoreStrategy();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -183,6 +183,12 @@ public class OssClient {
 | 
			
		||||
        return configKey;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取私有URL链接
 | 
			
		||||
     *
 | 
			
		||||
     * @param objectKey 对象KEY
 | 
			
		||||
     * @param second    授权时间
 | 
			
		||||
     */
 | 
			
		||||
    public String getPrivateUrl(String objectKey, Integer second) {
 | 
			
		||||
        GeneratePresignedUrlRequest generatePresignedUrlRequest =
 | 
			
		||||
            new GeneratePresignedUrlRequest(properties.getBucketName(), objectKey)
 | 
			
		||||
@@ -192,6 +198,13 @@ public class OssClient {
 | 
			
		||||
        return url.toString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 检查配置是否相同
 | 
			
		||||
     */
 | 
			
		||||
    public boolean checkPropertiesSame(OssProperties properties) {
 | 
			
		||||
        return this.properties.equals(properties);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取当前桶权限类型
 | 
			
		||||
     *
 | 
			
		||||
 
 | 
			
		||||
@@ -24,21 +24,6 @@ public class OssFactory {
 | 
			
		||||
 | 
			
		||||
    private static final Map<String, OssClient> CLIENT_CACHE = new ConcurrentHashMap<>();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 初始化工厂
 | 
			
		||||
     */
 | 
			
		||||
    public static void init() {
 | 
			
		||||
        log.info("初始化OSS工厂");
 | 
			
		||||
        RedisUtils.subscribe(OssConstant.DEFAULT_CONFIG_KEY, String.class, configKey -> {
 | 
			
		||||
            OssClient client = getClient(configKey);
 | 
			
		||||
            // 未初始化不处理
 | 
			
		||||
            if (client != null) {
 | 
			
		||||
                refresh(configKey);
 | 
			
		||||
                log.info("订阅刷新OSS配置 => " + configKey);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取默认实例
 | 
			
		||||
     */
 | 
			
		||||
@@ -55,25 +40,24 @@ public class OssFactory {
 | 
			
		||||
     * 根据类型获取实例
 | 
			
		||||
     */
 | 
			
		||||
    public static OssClient instance(String configKey) {
 | 
			
		||||
        OssClient client = getClient(configKey);
 | 
			
		||||
        if (client == null) {
 | 
			
		||||
            refresh(configKey);
 | 
			
		||||
            return getClient(configKey);
 | 
			
		||||
        }
 | 
			
		||||
        return client;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void refresh(String configKey) {
 | 
			
		||||
        String json = CacheUtils.get(CacheNames.SYS_OSS_CONFIG, configKey);
 | 
			
		||||
        if (json == null) {
 | 
			
		||||
            throw new OssException("系统异常, '" + configKey + "'配置信息不存在!");
 | 
			
		||||
        }
 | 
			
		||||
        OssProperties properties = JsonUtils.parseObject(json, OssProperties.class);
 | 
			
		||||
        CLIENT_CACHE.put(configKey, new OssClient(configKey, properties));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static OssClient getClient(String configKey) {
 | 
			
		||||
        return CLIENT_CACHE.get(configKey);
 | 
			
		||||
        OssClient client = CLIENT_CACHE.get(configKey);
 | 
			
		||||
        if (client == null) {
 | 
			
		||||
            CLIENT_CACHE.put(configKey, new OssClient(configKey, properties));
 | 
			
		||||
            log.info("创建OSS实例 key => {}", configKey);
 | 
			
		||||
            return CLIENT_CACHE.get(configKey);
 | 
			
		||||
        }
 | 
			
		||||
        // 配置不相同则重新构建
 | 
			
		||||
        if (!client.checkPropertiesSame(properties)) {
 | 
			
		||||
            CLIENT_CACHE.put(configKey, new OssClient(configKey, properties));
 | 
			
		||||
            log.info("重载OSS实例 key => {}", configKey);
 | 
			
		||||
            return CLIENT_CACHE.get(configKey);
 | 
			
		||||
        }
 | 
			
		||||
        return client;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
package com.ruoyi.common.ratelimiter.annotation;
 | 
			
		||||
 | 
			
		||||
import com.ruoyi.common.core.constant.CacheConstants;
 | 
			
		||||
import com.ruoyi.common.core.constant.GlobalConstants;
 | 
			
		||||
import com.ruoyi.common.ratelimiter.enums.LimitType;
 | 
			
		||||
 | 
			
		||||
import java.lang.annotation.*;
 | 
			
		||||
@@ -17,7 +17,7 @@ public @interface RateLimiter {
 | 
			
		||||
    /**
 | 
			
		||||
     * 限流key
 | 
			
		||||
     */
 | 
			
		||||
    String key() default CacheConstants.RATE_LIMIT_KEY;
 | 
			
		||||
    String key() default GlobalConstants.RATE_LIMIT_KEY;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 限流时间,单位秒
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,10 @@
 | 
			
		||||
package com.ruoyi.common.satoken.config;
 | 
			
		||||
 | 
			
		||||
import cn.dev33.satoken.dao.SaTokenDao;
 | 
			
		||||
import cn.dev33.satoken.jwt.StpLogicJwtForSimple;
 | 
			
		||||
import cn.dev33.satoken.stp.StpInterface;
 | 
			
		||||
import cn.dev33.satoken.stp.StpLogic;
 | 
			
		||||
import com.ruoyi.common.satoken.core.dao.PlusSaTokenDao;
 | 
			
		||||
import com.ruoyi.common.satoken.core.service.SaPermissionImpl;
 | 
			
		||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
 | 
			
		||||
import org.springframework.context.annotation.Bean;
 | 
			
		||||
@@ -30,4 +32,12 @@ public class SaTokenConfig implements WebMvcConfigurer {
 | 
			
		||||
        return new SaPermissionImpl();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 自定义dao层存储
 | 
			
		||||
     */
 | 
			
		||||
    @Bean
 | 
			
		||||
    public SaTokenDao saTokenDao() {
 | 
			
		||||
        return new PlusSaTokenDao();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,6 @@ package com.ruoyi.common.satoken.core.dao;
 | 
			
		||||
import cn.dev33.satoken.dao.SaTokenDao;
 | 
			
		||||
import cn.dev33.satoken.util.SaFoxUtil;
 | 
			
		||||
import com.ruoyi.common.redis.utils.RedisUtils;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
 | 
			
		||||
import java.time.Duration;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
@@ -15,7 +14,6 @@ import java.util.List;
 | 
			
		||||
 *
 | 
			
		||||
 * @author Lion Li
 | 
			
		||||
 */
 | 
			
		||||
@Component
 | 
			
		||||
public class PlusSaTokenDao implements SaTokenDao {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,11 @@
 | 
			
		||||
package com.ruoyi.common.satoken.utils;
 | 
			
		||||
 | 
			
		||||
import cn.dev33.satoken.context.SaHolder;
 | 
			
		||||
import cn.dev33.satoken.stp.SaLoginModel;
 | 
			
		||||
import cn.dev33.satoken.stp.StpUtil;
 | 
			
		||||
import cn.hutool.core.util.ObjectUtil;
 | 
			
		||||
import cn.hutool.json.JSONObject;
 | 
			
		||||
import com.ruoyi.common.core.constant.TenantConstants;
 | 
			
		||||
import com.ruoyi.common.core.constant.UserConstants;
 | 
			
		||||
import com.ruoyi.common.core.domain.model.LoginUser;
 | 
			
		||||
import com.ruoyi.common.core.enums.DeviceType;
 | 
			
		||||
@@ -12,6 +15,8 @@ import com.ruoyi.common.core.utils.StringUtils;
 | 
			
		||||
import lombok.AccessLevel;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 登录鉴权助手
 | 
			
		||||
 * <p>
 | 
			
		||||
@@ -37,8 +42,7 @@ public class LoginHelper {
 | 
			
		||||
     */
 | 
			
		||||
    public static void login(LoginUser loginUser) {
 | 
			
		||||
        SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser);
 | 
			
		||||
        StpUtil.login(loginUser.getLoginId());
 | 
			
		||||
        setLoginUser(loginUser);
 | 
			
		||||
        StpUtil.login(loginUser.getLoginId(), new SaLoginModel().setExtra(LOGIN_USER_KEY, loginUser));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -49,15 +53,9 @@ public class LoginHelper {
 | 
			
		||||
     */
 | 
			
		||||
    public static void loginByDevice(LoginUser loginUser, DeviceType deviceType) {
 | 
			
		||||
        SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser);
 | 
			
		||||
        StpUtil.login(loginUser.getLoginId(), deviceType.getDevice());
 | 
			
		||||
        setLoginUser(loginUser);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 设置用户数据(多级缓存)
 | 
			
		||||
     */
 | 
			
		||||
    public static void setLoginUser(LoginUser loginUser) {
 | 
			
		||||
        StpUtil.getTokenSession().set(LOGIN_USER_KEY, loginUser);
 | 
			
		||||
        StpUtil.login(loginUser.getLoginId(), new SaLoginModel()
 | 
			
		||||
            .setDevice(deviceType.getDevice())
 | 
			
		||||
            .setExtra(LOGIN_USER_KEY, loginUser));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -68,7 +66,7 @@ public class LoginHelper {
 | 
			
		||||
        if (loginUser != null) {
 | 
			
		||||
            return loginUser;
 | 
			
		||||
        }
 | 
			
		||||
        loginUser = (LoginUser) StpUtil.getTokenSession().get(LOGIN_USER_KEY);
 | 
			
		||||
        loginUser = ((JSONObject) StpUtil.getExtra(LOGIN_USER_KEY)).toBean(LoginUser.class);
 | 
			
		||||
        SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser);
 | 
			
		||||
        return loginUser;
 | 
			
		||||
    }
 | 
			
		||||
@@ -96,6 +94,19 @@ public class LoginHelper {
 | 
			
		||||
        return loginUser.getUserId();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取租户ID
 | 
			
		||||
     */
 | 
			
		||||
    public static String getTenantId() {
 | 
			
		||||
        LoginUser loginUser;
 | 
			
		||||
        try {
 | 
			
		||||
            loginUser = getLoginUser();
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        return loginUser.getTenantId();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取部门ID
 | 
			
		||||
     */
 | 
			
		||||
@@ -119,17 +130,31 @@ public class LoginHelper {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 是否为管理员
 | 
			
		||||
     * 是否为超级管理员
 | 
			
		||||
     *
 | 
			
		||||
     * @param userId 用户ID
 | 
			
		||||
     * @return 结果
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean isAdmin(Long userId) {
 | 
			
		||||
    public static boolean isSuperAdmin(Long userId) {
 | 
			
		||||
        return UserConstants.SUPER_ADMIN_ID.equals(userId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean isAdmin() {
 | 
			
		||||
        return isAdmin(getUserId());
 | 
			
		||||
    public static boolean isSuperAdmin() {
 | 
			
		||||
        return isSuperAdmin(getUserId());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 是否为超级管理员
 | 
			
		||||
     *
 | 
			
		||||
     * @param rolePermission 角色权限标识组
 | 
			
		||||
     * @return 结果
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean isTenantAdmin(Set<String> rolePermission) {
 | 
			
		||||
        return rolePermission.contains(TenantConstants.TENANT_ADMIN_ROLE_KEY);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean isTenantAdmin() {
 | 
			
		||||
        return isTenantAdmin(getLoginUser().getRolePermission());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,2 +1 @@
 | 
			
		||||
com.ruoyi.common.satoken.core.dao.PlusSaTokenDao
 | 
			
		||||
com.ruoyi.common.satoken.config.SaTokenConfig
 | 
			
		||||
 
 | 
			
		||||
@@ -22,12 +22,6 @@
 | 
			
		||||
            <artifactId>ruoyi-common-satoken</artifactId>
 | 
			
		||||
        </dependency>
 | 
			
		||||
 | 
			
		||||
        <!-- Sa-Token 权限认证, 在线文档:http://sa-token.dev33.cn/ -->
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>cn.dev33</groupId>
 | 
			
		||||
            <artifactId>sa-token-spring-boot3-starter</artifactId>
 | 
			
		||||
            <version>${satoken.version}</version>
 | 
			
		||||
        </dependency>
 | 
			
		||||
    </dependencies>
 | 
			
		||||
 | 
			
		||||
</project>
 | 
			
		||||
 
 | 
			
		||||
@@ -53,7 +53,7 @@ public class TencentSmsTemplate implements SmsTemplate {
 | 
			
		||||
            throw new SmsException("模板ID不能为空");
 | 
			
		||||
        }
 | 
			
		||||
        SendSmsRequest req = new SendSmsRequest();
 | 
			
		||||
        Set<String> set = Arrays.stream(phones.split(",")).map(p -> "+86" + p).collect(Collectors.toSet());
 | 
			
		||||
        Set<String> set = Arrays.stream(phones.split(StringUtils.SEPARATOR)).map(p -> "+86" + p).collect(Collectors.toSet());
 | 
			
		||||
        req.setPhoneNumberSet(ArrayUtil.toArray(set, String.class));
 | 
			
		||||
        if (CollUtil.isNotEmpty(param)) {
 | 
			
		||||
            req.setTemplateParamSet(ArrayUtil.toArray(param.values(), String.class));
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										37
									
								
								ruoyi-common/ruoyi-common-tenant/pom.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								ruoyi-common/ruoyi-common-tenant/pom.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
 | 
			
		||||
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 | 
			
		||||
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 | 
			
		||||
    <parent>
 | 
			
		||||
        <groupId>com.ruoyi</groupId>
 | 
			
		||||
        <artifactId>ruoyi-common</artifactId>
 | 
			
		||||
        <version>${revision}</version>
 | 
			
		||||
        <relativePath>../pom.xml</relativePath>
 | 
			
		||||
    </parent>
 | 
			
		||||
    <modelVersion>4.0.0</modelVersion>
 | 
			
		||||
 | 
			
		||||
    <artifactId>ruoyi-common-tenant</artifactId>
 | 
			
		||||
 | 
			
		||||
    <description>
 | 
			
		||||
        ruoyi-common-tenant 租户模块
 | 
			
		||||
    </description>
 | 
			
		||||
 | 
			
		||||
    <dependencies>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>com.ruoyi</groupId>
 | 
			
		||||
            <artifactId>ruoyi-common-mybatis</artifactId>
 | 
			
		||||
        </dependency>
 | 
			
		||||
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>com.ruoyi</groupId>
 | 
			
		||||
            <artifactId>ruoyi-common-redis</artifactId>
 | 
			
		||||
        </dependency>
 | 
			
		||||
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>com.alibaba</groupId>
 | 
			
		||||
            <artifactId>transmittable-thread-local</artifactId>
 | 
			
		||||
        </dependency>
 | 
			
		||||
 | 
			
		||||
    </dependencies>
 | 
			
		||||
 | 
			
		||||
</project>
 | 
			
		||||
@@ -0,0 +1,100 @@
 | 
			
		||||
package com.ruoyi.common.tenant.config;
 | 
			
		||||
 | 
			
		||||
import cn.dev33.satoken.dao.SaTokenDao;
 | 
			
		||||
import cn.hutool.core.util.ObjectUtil;
 | 
			
		||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
 | 
			
		||||
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
 | 
			
		||||
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
 | 
			
		||||
import com.ruoyi.common.core.utils.reflect.ReflectUtils;
 | 
			
		||||
import com.ruoyi.common.mybatis.config.MybatisPlusConfig;
 | 
			
		||||
import com.ruoyi.common.redis.config.RedisConfig;
 | 
			
		||||
import com.ruoyi.common.redis.config.properties.RedissonProperties;
 | 
			
		||||
import com.ruoyi.common.tenant.core.TenantSaTokenDao;
 | 
			
		||||
import com.ruoyi.common.tenant.handle.PlusTenantLineHandler;
 | 
			
		||||
import com.ruoyi.common.tenant.handle.TenantKeyPrefixHandler;
 | 
			
		||||
import com.ruoyi.common.tenant.manager.TenantSpringCacheManager;
 | 
			
		||||
import com.ruoyi.common.tenant.properties.TenantProperties;
 | 
			
		||||
import org.redisson.config.ClusterServersConfig;
 | 
			
		||||
import org.redisson.config.SingleServerConfig;
 | 
			
		||||
import org.redisson.spring.starter.RedissonAutoConfigurationCustomizer;
 | 
			
		||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
 | 
			
		||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 | 
			
		||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
 | 
			
		||||
import org.springframework.cache.CacheManager;
 | 
			
		||||
import org.springframework.context.annotation.Bean;
 | 
			
		||||
import org.springframework.context.annotation.Primary;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 租户配置类
 | 
			
		||||
 *
 | 
			
		||||
 * @author Lion Li
 | 
			
		||||
 */
 | 
			
		||||
@EnableConfigurationProperties(TenantProperties.class)
 | 
			
		||||
@AutoConfiguration(after = {RedisConfig.class, MybatisPlusConfig.class})
 | 
			
		||||
@ConditionalOnProperty(value = "tenant.enable", havingValue = "true")
 | 
			
		||||
public class TenantConfig {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 初始化租户配置
 | 
			
		||||
     */
 | 
			
		||||
    @Bean
 | 
			
		||||
    public boolean tenantInit(MybatisPlusInterceptor mybatisPlusInterceptor,
 | 
			
		||||
                              TenantProperties tenantProperties) {
 | 
			
		||||
        List<InnerInterceptor> interceptors = new ArrayList<>();
 | 
			
		||||
        // 多租户插件 必须放到第一位
 | 
			
		||||
        interceptors.add(tenantLineInnerInterceptor(tenantProperties));
 | 
			
		||||
        interceptors.addAll(mybatisPlusInterceptor.getInterceptors());
 | 
			
		||||
        mybatisPlusInterceptor.setInterceptors(interceptors);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 多租户插件
 | 
			
		||||
     */
 | 
			
		||||
    public TenantLineInnerInterceptor tenantLineInnerInterceptor(TenantProperties tenantProperties) {
 | 
			
		||||
        return new TenantLineInnerInterceptor(new PlusTenantLineHandler(tenantProperties));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Bean
 | 
			
		||||
    public RedissonAutoConfigurationCustomizer tenantRedissonCustomizer(RedissonProperties redissonProperties) {
 | 
			
		||||
        return config -> {
 | 
			
		||||
            TenantKeyPrefixHandler nameMapper = new TenantKeyPrefixHandler(redissonProperties.getKeyPrefix());
 | 
			
		||||
            SingleServerConfig singleServerConfig = ReflectUtils.invokeGetter(config, "singleServerConfig");
 | 
			
		||||
            if (ObjectUtil.isNotNull(singleServerConfig)) {
 | 
			
		||||
                // 使用单机模式
 | 
			
		||||
                // 设置多租户 redis key前缀
 | 
			
		||||
                singleServerConfig.setNameMapper(nameMapper);
 | 
			
		||||
                ReflectUtils.invokeSetter(config, "singleServerConfig", singleServerConfig);
 | 
			
		||||
            }
 | 
			
		||||
            ClusterServersConfig clusterServersConfig = ReflectUtils.invokeGetter(config, "clusterServersConfig");
 | 
			
		||||
            // 集群配置方式 参考下方注释
 | 
			
		||||
            if (ObjectUtil.isNotNull(clusterServersConfig)) {
 | 
			
		||||
                // 设置多租户 redis key前缀
 | 
			
		||||
                clusterServersConfig.setNameMapper(nameMapper);
 | 
			
		||||
                ReflectUtils.invokeSetter(config, "clusterServersConfig", clusterServersConfig);
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 多租户缓存管理器
 | 
			
		||||
     */
 | 
			
		||||
    @Primary
 | 
			
		||||
    @Bean
 | 
			
		||||
    public CacheManager tenantCacheManager() {
 | 
			
		||||
        return new TenantSpringCacheManager();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 多租户鉴权dao实现
 | 
			
		||||
     */
 | 
			
		||||
    @Primary
 | 
			
		||||
    @Bean
 | 
			
		||||
    public SaTokenDao tenantSaTokenDao() {
 | 
			
		||||
        return new TenantSaTokenDao();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,21 @@
 | 
			
		||||
package com.ruoyi.common.tenant.core;
 | 
			
		||||
 | 
			
		||||
import com.ruoyi.common.mybatis.core.domain.BaseEntity;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.EqualsAndHashCode;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 租户基类
 | 
			
		||||
 *
 | 
			
		||||
 * @author Michelle.Chung
 | 
			
		||||
 */
 | 
			
		||||
@Data
 | 
			
		||||
@EqualsAndHashCode(callSuper = true)
 | 
			
		||||
public class TenantEntity extends BaseEntity {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 租户编号
 | 
			
		||||
     */
 | 
			
		||||
    private String tenantId;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,114 @@
 | 
			
		||||
package com.ruoyi.common.tenant.core;
 | 
			
		||||
 | 
			
		||||
import com.ruoyi.common.core.constant.GlobalConstants;
 | 
			
		||||
import com.ruoyi.common.satoken.core.dao.PlusSaTokenDao;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * SaToken 认证数据持久层 适配多租户
 | 
			
		||||
 *
 | 
			
		||||
 * @author Lion Li
 | 
			
		||||
 */
 | 
			
		||||
public class TenantSaTokenDao extends PlusSaTokenDao {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String get(String key) {
 | 
			
		||||
        return super.get(GlobalConstants.GLOBAL_REDIS_KEY + key);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void set(String key, String value, long timeout) {
 | 
			
		||||
        super.set(GlobalConstants.GLOBAL_REDIS_KEY + key, value, timeout);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 修修改指定key-value键值对 (过期时间不变)
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    public void update(String key, String value) {
 | 
			
		||||
        super.update(GlobalConstants.GLOBAL_REDIS_KEY + key, value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 删除Value
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    public void delete(String key) {
 | 
			
		||||
        super.delete(GlobalConstants.GLOBAL_REDIS_KEY + key);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取Value的剩余存活时间 (单位: 秒)
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    public long getTimeout(String key) {
 | 
			
		||||
        return super.getTimeout(GlobalConstants.GLOBAL_REDIS_KEY + key);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 修改Value的剩余存活时间 (单位: 秒)
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    public void updateTimeout(String key, long timeout) {
 | 
			
		||||
        super.updateTimeout(GlobalConstants.GLOBAL_REDIS_KEY + key, timeout);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取Object,如无返空
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    public Object getObject(String key) {
 | 
			
		||||
        return super.getObject(GlobalConstants.GLOBAL_REDIS_KEY + key);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 写入Object,并设定存活时间 (单位: 秒)
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setObject(String key, Object object, long timeout) {
 | 
			
		||||
        super.setObject(GlobalConstants.GLOBAL_REDIS_KEY + key, object, timeout);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 更新Object (过期时间不变)
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    public void updateObject(String key, Object object) {
 | 
			
		||||
        super.updateObject(GlobalConstants.GLOBAL_REDIS_KEY + key, object);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 删除Object
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    public void deleteObject(String key) {
 | 
			
		||||
        super.deleteObject(GlobalConstants.GLOBAL_REDIS_KEY + key);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取Object的剩余存活时间 (单位: 秒)
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    public long getObjectTimeout(String key) {
 | 
			
		||||
        return super.getObjectTimeout(GlobalConstants.GLOBAL_REDIS_KEY + key);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 修改Object的剩余存活时间 (单位: 秒)
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    public void updateObjectTimeout(String key, long timeout) {
 | 
			
		||||
        super.updateObjectTimeout(GlobalConstants.GLOBAL_REDIS_KEY + key, timeout);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 搜索数据
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    public List<String> searchData(String prefix, String keyword, int start, int size, boolean sortType) {
 | 
			
		||||
        return super.searchData(GlobalConstants.GLOBAL_REDIS_KEY + prefix, keyword, start, size, sortType);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,20 @@
 | 
			
		||||
package com.ruoyi.common.tenant.exception;
 | 
			
		||||
 | 
			
		||||
import com.ruoyi.common.core.exception.base.BaseException;
 | 
			
		||||
 | 
			
		||||
import java.io.Serial;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 租户异常类
 | 
			
		||||
 *
 | 
			
		||||
 * @author Lion Li
 | 
			
		||||
 */
 | 
			
		||||
public class TenantException extends BaseException {
 | 
			
		||||
 | 
			
		||||
    @Serial
 | 
			
		||||
    private static final long serialVersionUID = 1L;
 | 
			
		||||
 | 
			
		||||
    public TenantException(String code, Object... args) {
 | 
			
		||||
        super("tenant", code, args, null);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,57 @@
 | 
			
		||||
package com.ruoyi.common.tenant.handle;
 | 
			
		||||
 | 
			
		||||
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
 | 
			
		||||
import com.ruoyi.common.core.utils.StringUtils;
 | 
			
		||||
import com.ruoyi.common.satoken.utils.LoginHelper;
 | 
			
		||||
import com.ruoyi.common.tenant.helper.TenantHelper;
 | 
			
		||||
import com.ruoyi.common.tenant.properties.TenantProperties;
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import net.sf.jsqlparser.expression.Expression;
 | 
			
		||||
import net.sf.jsqlparser.expression.LongValue;
 | 
			
		||||
import net.sf.jsqlparser.expression.NullValue;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 自定义租户处理器
 | 
			
		||||
 *
 | 
			
		||||
 * @author Lion Li
 | 
			
		||||
 */
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
public class PlusTenantLineHandler implements TenantLineHandler {
 | 
			
		||||
 | 
			
		||||
    private final TenantProperties tenantProperties;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Expression getTenantId() {
 | 
			
		||||
        String tenantId = LoginHelper.getTenantId();
 | 
			
		||||
        if (StringUtils.isBlank(tenantId)) {
 | 
			
		||||
            return new NullValue();
 | 
			
		||||
        }
 | 
			
		||||
        String dynamicTenantId = TenantHelper.getDynamic();
 | 
			
		||||
        if (StringUtils.isNotBlank(dynamicTenantId)) {
 | 
			
		||||
            // 返回动态租户
 | 
			
		||||
            return new LongValue(dynamicTenantId);
 | 
			
		||||
        }
 | 
			
		||||
        // 返回固定租户
 | 
			
		||||
        return new LongValue(tenantId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean ignoreTable(String tableName) {
 | 
			
		||||
        String tenantId = LoginHelper.getTenantId();
 | 
			
		||||
        // 判断是否有租户
 | 
			
		||||
        if (StringUtils.isNotBlank(tenantId)) {
 | 
			
		||||
            // 不需要过滤租户的表
 | 
			
		||||
            List<String> excludes = tenantProperties.getExcludes();
 | 
			
		||||
            // 非业务表
 | 
			
		||||
            excludes.addAll(List.of(
 | 
			
		||||
                    "gen_table",
 | 
			
		||||
                    "gen_table_column"
 | 
			
		||||
            ));
 | 
			
		||||
            return excludes.contains(tableName);
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,58 @@
 | 
			
		||||
package com.ruoyi.common.tenant.handle;
 | 
			
		||||
 | 
			
		||||
import com.ruoyi.common.core.constant.GlobalConstants;
 | 
			
		||||
import com.ruoyi.common.core.utils.StringUtils;
 | 
			
		||||
import com.ruoyi.common.redis.handler.KeyPrefixHandler;
 | 
			
		||||
import com.ruoyi.common.tenant.helper.TenantHelper;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 多租户redis缓存key前缀处理
 | 
			
		||||
 *
 | 
			
		||||
 * @author Lion Li
 | 
			
		||||
 */
 | 
			
		||||
public class TenantKeyPrefixHandler extends KeyPrefixHandler {
 | 
			
		||||
 | 
			
		||||
    public TenantKeyPrefixHandler(String keyPrefix) {
 | 
			
		||||
        super(keyPrefix);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 增加前缀
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    public String map(String name) {
 | 
			
		||||
        if (StringUtils.isBlank(name)) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        if (StringUtils.contains(name, GlobalConstants.GLOBAL_REDIS_KEY)) {
 | 
			
		||||
            return super.map(name);
 | 
			
		||||
        }
 | 
			
		||||
        String tenantId = TenantHelper.getTenantId();
 | 
			
		||||
        if (StringUtils.startsWith(name, tenantId)) {
 | 
			
		||||
            // 如果存在则直接返回
 | 
			
		||||
            return super.map(name);
 | 
			
		||||
        }
 | 
			
		||||
        return super.map(tenantId + ":" + name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 去除前缀
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    public String unmap(String name) {
 | 
			
		||||
        String unmap = super.unmap(name);
 | 
			
		||||
        if (StringUtils.isBlank(unmap)) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        if (StringUtils.contains(name, GlobalConstants.GLOBAL_REDIS_KEY)) {
 | 
			
		||||
            return super.unmap(name);
 | 
			
		||||
        }
 | 
			
		||||
        String tenantId = TenantHelper.getTenantId();
 | 
			
		||||
        if (StringUtils.startsWith(unmap, tenantId)) {
 | 
			
		||||
            // 如果存在则删除
 | 
			
		||||
            return unmap.substring((tenantId + ":").length());
 | 
			
		||||
        }
 | 
			
		||||
        return unmap;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,112 @@
 | 
			
		||||
package com.ruoyi.common.tenant.helper;
 | 
			
		||||
 | 
			
		||||
import cn.dev33.satoken.context.SaHolder;
 | 
			
		||||
import cn.dev33.satoken.spring.SpringMVCUtil;
 | 
			
		||||
import com.alibaba.ttl.TransmittableThreadLocal;
 | 
			
		||||
import com.baomidou.mybatisplus.core.plugins.IgnoreStrategy;
 | 
			
		||||
import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
 | 
			
		||||
import com.ruoyi.common.core.constant.GlobalConstants;
 | 
			
		||||
import com.ruoyi.common.core.utils.SpringUtils;
 | 
			
		||||
import com.ruoyi.common.core.utils.StringUtils;
 | 
			
		||||
import com.ruoyi.common.redis.utils.RedisUtils;
 | 
			
		||||
import com.ruoyi.common.satoken.utils.LoginHelper;
 | 
			
		||||
import com.ruoyi.common.tenant.properties.TenantProperties;
 | 
			
		||||
import lombok.AccessLevel;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 租户助手
 | 
			
		||||
 *
 | 
			
		||||
 * @author Lion Li
 | 
			
		||||
 */
 | 
			
		||||
@Slf4j
 | 
			
		||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
 | 
			
		||||
public class TenantHelper {
 | 
			
		||||
 | 
			
		||||
    private static final TenantProperties PROPERTIES = SpringUtils.getBean(TenantProperties.class);
 | 
			
		||||
 | 
			
		||||
    private static final String DYNAMIC_TENANT_KEY = GlobalConstants.GLOBAL_REDIS_KEY + "dynamicTenant";
 | 
			
		||||
 | 
			
		||||
    private static final ThreadLocal<String> TEMP_DYNAMIC_TENANT = new TransmittableThreadLocal<>();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 租户功能是否启用
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean isEnable() {
 | 
			
		||||
        return PROPERTIES.getEnable();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 开启忽略租户(开启后需手动调用 {@link #disableIgnore()} 关闭)
 | 
			
		||||
     */
 | 
			
		||||
    public static void enableIgnore() {
 | 
			
		||||
        InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().tenantLine(true).build());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 关闭忽略租户
 | 
			
		||||
     */
 | 
			
		||||
    public static void disableIgnore() {
 | 
			
		||||
        InterceptorIgnoreHelper.clearIgnoreStrategy();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 设置动态租户(一直有效 需要手动清理)
 | 
			
		||||
     * <p>
 | 
			
		||||
     * 如果为非web环境 那么只在当前线程内生效
 | 
			
		||||
     */
 | 
			
		||||
    public static void setDynamic(String tenantId) {
 | 
			
		||||
        if (!SpringMVCUtil.isWeb()) {
 | 
			
		||||
            TEMP_DYNAMIC_TENANT.set(tenantId);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        String cacheKey = DYNAMIC_TENANT_KEY + ":" + LoginHelper.getTenantId();
 | 
			
		||||
        RedisUtils.setCacheObject(cacheKey, tenantId);
 | 
			
		||||
        SaHolder.getStorage().set(cacheKey, tenantId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取动态租户(一直有效 需要手动清理)
 | 
			
		||||
     * <p>
 | 
			
		||||
     * 如果为非web环境 那么只在当前线程内生效
 | 
			
		||||
     */
 | 
			
		||||
    public static String getDynamic() {
 | 
			
		||||
        if (!SpringMVCUtil.isWeb()) {
 | 
			
		||||
            return TEMP_DYNAMIC_TENANT.get();
 | 
			
		||||
        }
 | 
			
		||||
        String cacheKey = DYNAMIC_TENANT_KEY + ":" + LoginHelper.getTenantId();
 | 
			
		||||
        String tenantId = (String) SaHolder.getStorage().get(cacheKey);
 | 
			
		||||
        if (StringUtils.isNotBlank(tenantId)) {
 | 
			
		||||
            return tenantId;
 | 
			
		||||
        }
 | 
			
		||||
        tenantId = RedisUtils.getCacheObject(cacheKey);
 | 
			
		||||
        SaHolder.getStorage().set(cacheKey, tenantId);
 | 
			
		||||
        return tenantId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 清除动态租户
 | 
			
		||||
     */
 | 
			
		||||
    public static void clearDynamic() {
 | 
			
		||||
        if (!SpringMVCUtil.isWeb()) {
 | 
			
		||||
            TEMP_DYNAMIC_TENANT.remove();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        String cacheKey = DYNAMIC_TENANT_KEY + ":" + LoginHelper.getTenantId();
 | 
			
		||||
        RedisUtils.deleteObject(cacheKey);
 | 
			
		||||
        SaHolder.getStorage().delete(cacheKey);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取当前租户id(动态租户优先)
 | 
			
		||||
     */
 | 
			
		||||
    public static String getTenantId() {
 | 
			
		||||
        String tenantId = TenantHelper.getDynamic();
 | 
			
		||||
        if (StringUtils.isBlank(tenantId)) {
 | 
			
		||||
            tenantId = LoginHelper.getTenantId();
 | 
			
		||||
        }
 | 
			
		||||
        return tenantId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,32 @@
 | 
			
		||||
package com.ruoyi.common.tenant.manager;
 | 
			
		||||
 | 
			
		||||
import com.ruoyi.common.core.constant.GlobalConstants;
 | 
			
		||||
import com.ruoyi.common.core.utils.StringUtils;
 | 
			
		||||
import com.ruoyi.common.redis.manager.PlusSpringCacheManager;
 | 
			
		||||
import com.ruoyi.common.tenant.helper.TenantHelper;
 | 
			
		||||
import org.springframework.cache.Cache;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 重写 cacheName 处理方法 支持多租户
 | 
			
		||||
 *
 | 
			
		||||
 * @author Lion Li
 | 
			
		||||
 */
 | 
			
		||||
public class TenantSpringCacheManager extends PlusSpringCacheManager {
 | 
			
		||||
 | 
			
		||||
    public TenantSpringCacheManager() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Cache getCache(String name) {
 | 
			
		||||
        if (StringUtils.contains(name, GlobalConstants.GLOBAL_REDIS_KEY)) {
 | 
			
		||||
            return super.getCache(name);
 | 
			
		||||
        }
 | 
			
		||||
        String tenantId = TenantHelper.getTenantId();
 | 
			
		||||
        if (StringUtils.startsWith(name, tenantId)) {
 | 
			
		||||
            // 如果存在则直接返回
 | 
			
		||||
            return super.getCache(name);
 | 
			
		||||
        }
 | 
			
		||||
        return super.getCache(tenantId + ":" + name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,27 @@
 | 
			
		||||
package com.ruoyi.common.tenant.properties;
 | 
			
		||||
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import org.springframework.boot.context.properties.ConfigurationProperties;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 租户 配置属性
 | 
			
		||||
 *
 | 
			
		||||
 * @author Lion Li
 | 
			
		||||
 */
 | 
			
		||||
@Data
 | 
			
		||||
@ConfigurationProperties(prefix = "tenant")
 | 
			
		||||
public class TenantProperties {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 是否启用
 | 
			
		||||
     */
 | 
			
		||||
    private Boolean enable;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 排除表
 | 
			
		||||
     */
 | 
			
		||||
    private List<String> excludes;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1 @@
 | 
			
		||||
com.ruoyi.common.tenant.config.TenantConfig
 | 
			
		||||
@@ -30,7 +30,7 @@ public class FilterConfig {
 | 
			
		||||
        FilterRegistrationBean registration = new FilterRegistrationBean();
 | 
			
		||||
        registration.setDispatcherTypes(DispatcherType.REQUEST);
 | 
			
		||||
        registration.setFilter(new XssFilter());
 | 
			
		||||
        registration.addUrlPatterns(StringUtils.split(xssProperties.getUrlPatterns(), ","));
 | 
			
		||||
        registration.addUrlPatterns(StringUtils.split(xssProperties.getUrlPatterns(), StringUtils.SEPARATOR));
 | 
			
		||||
        registration.setName("xssFilter");
 | 
			
		||||
        registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);
 | 
			
		||||
        Map<String, String> initParameters = new HashMap<>();
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,8 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
 | 
			
		||||
@ConfigurationProperties(prefix = "captcha")
 | 
			
		||||
public class CaptchaProperties {
 | 
			
		||||
 | 
			
		||||
    private Boolean enable;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 验证码类型
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@ public class XssFilter implements Filter {
 | 
			
		||||
    public void init(FilterConfig filterConfig) throws ServletException {
 | 
			
		||||
        String tempExcludes = filterConfig.getInitParameter("excludes");
 | 
			
		||||
        if (StringUtils.isNotEmpty(tempExcludes)) {
 | 
			
		||||
            String[] url = tempExcludes.split(",");
 | 
			
		||||
            String[] url = tempExcludes.split(StringUtils.SEPARATOR);
 | 
			
		||||
            for (int i = 0; url != null && i < url.length; i++) {
 | 
			
		||||
                excludes.add(url[i]);
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -45,7 +45,7 @@ import java.util.concurrent.TimeUnit;
 | 
			
		||||
@RequestMapping("/demo/demo")
 | 
			
		||||
public class TestDemoController extends BaseController {
 | 
			
		||||
 | 
			
		||||
    private final ITestDemoService iTestDemoService;
 | 
			
		||||
    private final ITestDemoService testDemoService;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 查询测试单表列表
 | 
			
		||||
@@ -53,7 +53,7 @@ public class TestDemoController extends BaseController {
 | 
			
		||||
    @SaCheckPermission("demo:demo:list")
 | 
			
		||||
    @GetMapping("/list")
 | 
			
		||||
    public TableDataInfo<TestDemoVo> list(@Validated(QueryGroup.class) TestDemoBo bo, PageQuery pageQuery) {
 | 
			
		||||
        return iTestDemoService.queryPageList(bo, pageQuery);
 | 
			
		||||
        return testDemoService.queryPageList(bo, pageQuery);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -62,7 +62,7 @@ public class TestDemoController extends BaseController {
 | 
			
		||||
    @SaCheckPermission("demo:demo:list")
 | 
			
		||||
    @GetMapping("/page")
 | 
			
		||||
    public TableDataInfo<TestDemoVo> page(@Validated(QueryGroup.class) TestDemoBo bo, PageQuery pageQuery) {
 | 
			
		||||
        return iTestDemoService.customPageList(bo, pageQuery);
 | 
			
		||||
        return testDemoService.customPageList(bo, pageQuery);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -77,7 +77,7 @@ public class TestDemoController extends BaseController {
 | 
			
		||||
        ExcelResult<TestDemoImportVo> excelResult = ExcelUtil.importExcel(file.getInputStream(), TestDemoImportVo.class, true);
 | 
			
		||||
        List<TestDemoImportVo> volist = excelResult.getList();
 | 
			
		||||
        List<TestDemo> list = BeanUtil.copyToList(volist, TestDemo.class);
 | 
			
		||||
        iTestDemoService.saveBatch(list);
 | 
			
		||||
        testDemoService.saveBatch(list);
 | 
			
		||||
        return R.ok(excelResult.getAnalysis());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -88,7 +88,7 @@ public class TestDemoController extends BaseController {
 | 
			
		||||
    @Log(title = "测试单表", businessType = BusinessType.EXPORT)
 | 
			
		||||
    @PostMapping("/export")
 | 
			
		||||
    public void export(@Validated TestDemoBo bo, HttpServletResponse response) {
 | 
			
		||||
        List<TestDemoVo> list = iTestDemoService.queryList(bo);
 | 
			
		||||
        List<TestDemoVo> list = testDemoService.queryList(bo);
 | 
			
		||||
        // 测试雪花id导出
 | 
			
		||||
//        for (TestDemoVo vo : list) {
 | 
			
		||||
//            vo.setId(1234567891234567893L);
 | 
			
		||||
@@ -105,7 +105,7 @@ public class TestDemoController extends BaseController {
 | 
			
		||||
    @GetMapping("/{id}")
 | 
			
		||||
    public R<TestDemoVo> getInfo(@NotNull(message = "主键不能为空")
 | 
			
		||||
                                 @PathVariable("id") Long id) {
 | 
			
		||||
        return R.ok(iTestDemoService.queryById(id));
 | 
			
		||||
        return R.ok(testDemoService.queryById(id));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -119,7 +119,7 @@ public class TestDemoController extends BaseController {
 | 
			
		||||
        // 使用校验工具对标 @Validated(AddGroup.class) 注解
 | 
			
		||||
        // 用于在非 Controller 的地方校验对象
 | 
			
		||||
        ValidatorUtils.validate(bo, AddGroup.class);
 | 
			
		||||
        return toAjax(iTestDemoService.insertByBo(bo));
 | 
			
		||||
        return toAjax(testDemoService.insertByBo(bo));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -130,7 +130,7 @@ public class TestDemoController extends BaseController {
 | 
			
		||||
    @RepeatSubmit
 | 
			
		||||
    @PutMapping()
 | 
			
		||||
    public R<Void> edit(@Validated(EditGroup.class) @RequestBody TestDemoBo bo) {
 | 
			
		||||
        return toAjax(iTestDemoService.updateByBo(bo));
 | 
			
		||||
        return toAjax(testDemoService.updateByBo(bo));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -143,6 +143,6 @@ public class TestDemoController extends BaseController {
 | 
			
		||||
    @DeleteMapping("/{ids}")
 | 
			
		||||
    public R<Void> remove(@NotEmpty(message = "主键不能为空")
 | 
			
		||||
                          @PathVariable Long[] ids) {
 | 
			
		||||
        return toAjax(iTestDemoService.deleteWithValidByIds(Arrays.asList(ids), true));
 | 
			
		||||
        return toAjax(testDemoService.deleteWithValidByIds(Arrays.asList(ids), true));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -35,7 +35,7 @@ import java.util.List;
 | 
			
		||||
@RequestMapping("/demo/tree")
 | 
			
		||||
public class TestTreeController extends BaseController {
 | 
			
		||||
 | 
			
		||||
    private final ITestTreeService iTestTreeService;
 | 
			
		||||
    private final ITestTreeService testTreeService;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 查询测试树表列表
 | 
			
		||||
@@ -43,7 +43,7 @@ public class TestTreeController extends BaseController {
 | 
			
		||||
    @SaCheckPermission("demo:tree:list")
 | 
			
		||||
    @GetMapping("/list")
 | 
			
		||||
    public R<List<TestTreeVo>> list(@Validated(QueryGroup.class) TestTreeBo bo) {
 | 
			
		||||
        List<TestTreeVo> list = iTestTreeService.queryList(bo);
 | 
			
		||||
        List<TestTreeVo> list = testTreeService.queryList(bo);
 | 
			
		||||
        return R.ok(list);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -54,7 +54,7 @@ public class TestTreeController extends BaseController {
 | 
			
		||||
    @Log(title = "测试树表", businessType = BusinessType.EXPORT)
 | 
			
		||||
    @GetMapping("/export")
 | 
			
		||||
    public void export(@Validated TestTreeBo bo, HttpServletResponse response) {
 | 
			
		||||
        List<TestTreeVo> list = iTestTreeService.queryList(bo);
 | 
			
		||||
        List<TestTreeVo> list = testTreeService.queryList(bo);
 | 
			
		||||
        ExcelUtil.exportExcel(list, "测试树表", TestTreeVo.class, response);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -67,7 +67,7 @@ public class TestTreeController extends BaseController {
 | 
			
		||||
    @GetMapping("/{id}")
 | 
			
		||||
    public R<TestTreeVo> getInfo(@NotNull(message = "主键不能为空")
 | 
			
		||||
                                 @PathVariable("id") Long id) {
 | 
			
		||||
        return R.ok(iTestTreeService.queryById(id));
 | 
			
		||||
        return R.ok(testTreeService.queryById(id));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -78,7 +78,7 @@ public class TestTreeController extends BaseController {
 | 
			
		||||
    @RepeatSubmit
 | 
			
		||||
    @PostMapping()
 | 
			
		||||
    public R<Void> add(@Validated(AddGroup.class) @RequestBody TestTreeBo bo) {
 | 
			
		||||
        return toAjax(iTestTreeService.insertByBo(bo));
 | 
			
		||||
        return toAjax(testTreeService.insertByBo(bo));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -89,7 +89,7 @@ public class TestTreeController extends BaseController {
 | 
			
		||||
    @RepeatSubmit
 | 
			
		||||
    @PutMapping()
 | 
			
		||||
    public R<Void> edit(@Validated(EditGroup.class) @RequestBody TestTreeBo bo) {
 | 
			
		||||
        return toAjax(iTestTreeService.updateByBo(bo));
 | 
			
		||||
        return toAjax(testTreeService.updateByBo(bo));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -102,6 +102,6 @@ public class TestTreeController extends BaseController {
 | 
			
		||||
    @DeleteMapping("/{ids}")
 | 
			
		||||
    public R<Void> remove(@NotEmpty(message = "主键不能为空")
 | 
			
		||||
                          @PathVariable Long[] ids) {
 | 
			
		||||
        return toAjax(iTestTreeService.deleteWithValidByIds(Arrays.asList(ids), true));
 | 
			
		||||
        return toAjax(testTreeService.deleteWithValidByIds(Arrays.asList(ids), true));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ import com.baomidou.mybatisplus.annotation.TableId;
 | 
			
		||||
import com.baomidou.mybatisplus.annotation.TableLogic;
 | 
			
		||||
import com.baomidou.mybatisplus.annotation.TableName;
 | 
			
		||||
import com.baomidou.mybatisplus.annotation.Version;
 | 
			
		||||
import com.ruoyi.common.mybatis.core.domain.TreeEntity;
 | 
			
		||||
import com.ruoyi.common.mybatis.core.domain.BaseEntity;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.EqualsAndHashCode;
 | 
			
		||||
 | 
			
		||||
@@ -19,7 +19,7 @@ import java.io.Serial;
 | 
			
		||||
@Data
 | 
			
		||||
@EqualsAndHashCode(callSuper = true)
 | 
			
		||||
@TableName("test_tree")
 | 
			
		||||
public class TestTree extends TreeEntity<TestTree> {
 | 
			
		||||
public class TestTree extends BaseEntity {
 | 
			
		||||
 | 
			
		||||
    @Serial
 | 
			
		||||
    private static final long serialVersionUID = 1L;
 | 
			
		||||
@@ -30,6 +30,11 @@ public class TestTree extends TreeEntity<TestTree> {
 | 
			
		||||
    @TableId(value = "id")
 | 
			
		||||
    private Long id;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 父ID
 | 
			
		||||
     */
 | 
			
		||||
    private Long parentId;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 部门id
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
@@ -2,12 +2,11 @@ package com.ruoyi.demo.domain.bo;
 | 
			
		||||
 | 
			
		||||
import com.ruoyi.common.core.validate.AddGroup;
 | 
			
		||||
import com.ruoyi.common.core.validate.EditGroup;
 | 
			
		||||
import com.ruoyi.common.mybatis.core.domain.TreeEntity;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.EqualsAndHashCode;
 | 
			
		||||
 | 
			
		||||
import com.ruoyi.common.mybatis.core.domain.BaseEntity;
 | 
			
		||||
import jakarta.validation.constraints.NotBlank;
 | 
			
		||||
import jakarta.validation.constraints.NotNull;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.EqualsAndHashCode;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 测试树表业务对象 test_tree
 | 
			
		||||
@@ -18,7 +17,7 @@ import jakarta.validation.constraints.NotNull;
 | 
			
		||||
 | 
			
		||||
@Data
 | 
			
		||||
@EqualsAndHashCode(callSuper = true)
 | 
			
		||||
public class TestTreeBo extends TreeEntity<TestTreeBo> {
 | 
			
		||||
public class TestTreeBo extends BaseEntity {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 主键
 | 
			
		||||
@@ -26,6 +25,11 @@ public class TestTreeBo extends TreeEntity<TestTreeBo> {
 | 
			
		||||
    @NotNull(message = "主键不能为空", groups = {EditGroup.class})
 | 
			
		||||
    private Long id;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 父ID
 | 
			
		||||
     */
 | 
			
		||||
    private Long parentId;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 部门id
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
@@ -69,35 +69,30 @@ public interface GenConstants {
 | 
			
		||||
     * BO对象 不需要添加字段
 | 
			
		||||
     */
 | 
			
		||||
    String[] COLUMNNAME_NOT_ADD = {"create_dept", "create_by", "create_time", "del_flag", "update_by",
 | 
			
		||||
        "update_time", "version"};
 | 
			
		||||
        "update_time", "version", "tenant_id"};
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * BO对象 不需要编辑字段
 | 
			
		||||
     */
 | 
			
		||||
    String[] COLUMNNAME_NOT_EDIT = {"create_dept", "create_by", "create_time", "del_flag", "update_by",
 | 
			
		||||
        "update_time", "version"};
 | 
			
		||||
        "update_time", "version", "tenant_id"};
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * VO对象 不需要返回字段
 | 
			
		||||
     */
 | 
			
		||||
    String[] COLUMNNAME_NOT_LIST = {"create_dept", "create_by", "create_time", "del_flag", "update_by",
 | 
			
		||||
        "update_time", "version"};
 | 
			
		||||
        "update_time", "version", "tenant_id"};
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * BO对象 不需要查询字段
 | 
			
		||||
     */
 | 
			
		||||
    String[] COLUMNNAME_NOT_QUERY = {"id", "create_dept", "create_by", "create_time", "del_flag", "update_by",
 | 
			
		||||
        "update_time", "remark", "version"};
 | 
			
		||||
        "update_time", "remark", "version", "tenant_id"};
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Entity基类字段
 | 
			
		||||
     */
 | 
			
		||||
    String[] BASE_ENTITY = {"createDept", "createBy", "createTime", "updateBy", "updateTime"};
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Tree基类字段
 | 
			
		||||
     */
 | 
			
		||||
    String[] TREE_ENTITY = {"parentName", "parentId", "children"};
 | 
			
		||||
    String[] BASE_ENTITY = {"createDept", "createBy", "createTime", "updateBy", "updateTime", "tenantId"};
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 文本框
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,16 @@
 | 
			
		||||
package com.ruoyi.generator.domain;
 | 
			
		||||
 | 
			
		||||
import com.baomidou.mybatisplus.annotation.*;
 | 
			
		||||
import com.ruoyi.generator.constant.GenConstants;
 | 
			
		||||
import com.baomidou.mybatisplus.annotation.FieldStrategy;
 | 
			
		||||
import com.baomidou.mybatisplus.annotation.TableField;
 | 
			
		||||
import com.baomidou.mybatisplus.annotation.TableId;
 | 
			
		||||
import com.baomidou.mybatisplus.annotation.TableName;
 | 
			
		||||
import com.ruoyi.common.core.utils.StringUtils;
 | 
			
		||||
import com.ruoyi.common.mybatis.core.domain.BaseEntity;
 | 
			
		||||
import com.ruoyi.generator.constant.GenConstants;
 | 
			
		||||
import jakarta.validation.Valid;
 | 
			
		||||
import jakarta.validation.constraints.NotBlank;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.EqualsAndHashCode;
 | 
			
		||||
import org.apache.commons.lang3.ArrayUtils;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
@@ -183,10 +185,6 @@ public class GenTable extends BaseEntity {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean isSuperColumn(String tplCategory, String javaField) {
 | 
			
		||||
        if (isTree(tplCategory)) {
 | 
			
		||||
            return StringUtils.equalsAnyIgnoreCase(javaField,
 | 
			
		||||
                ArrayUtils.addAll(GenConstants.TREE_ENTITY, GenConstants.BASE_ENTITY));
 | 
			
		||||
        }
 | 
			
		||||
        return StringUtils.equalsAnyIgnoreCase(javaField, GenConstants.BASE_ENTITY);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -212,7 +212,7 @@ public class GenTableColumn extends BaseEntity {
 | 
			
		||||
                if (StringUtils.isNotEmpty(value)) {
 | 
			
		||||
                    Object startStr = value.subSequence(0, 1);
 | 
			
		||||
                    String endStr = value.substring(1);
 | 
			
		||||
                    sb.append("").append(startStr).append("=").append(endStr).append(",");
 | 
			
		||||
                    sb.append(StringUtils.EMPTY).append(startStr).append("=").append(endStr).append(StringUtils.SEPARATOR);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return sb.deleteCharAt(sb.length() - 1).toString();
 | 
			
		||||
 
 | 
			
		||||
@@ -59,7 +59,7 @@ public class GenUtils {
 | 
			
		||||
            column.setHtmlType(GenConstants.HTML_INPUT);
 | 
			
		||||
 | 
			
		||||
            // 如果是浮点型 统一用BigDecimal
 | 
			
		||||
            String[] str = StringUtils.split(StringUtils.substringBetween(column.getColumnType(), "(", ")"), ",");
 | 
			
		||||
            String[] str = StringUtils.split(StringUtils.substringBetween(column.getColumnType(), "(", ")"), StringUtils.SEPARATOR);
 | 
			
		||||
            if (str != null && str.length == 2 && Integer.parseInt(str[1]) > 0) {
 | 
			
		||||
                column.setJavaType(GenConstants.TYPE_BIGDECIMAL);
 | 
			
		||||
            }
 | 
			
		||||
@@ -168,7 +168,7 @@ public class GenUtils {
 | 
			
		||||
        boolean autoRemovePre = GenConfig.getAutoRemovePre();
 | 
			
		||||
        String tablePrefix = GenConfig.getTablePrefix();
 | 
			
		||||
        if (autoRemovePre && StringUtils.isNotEmpty(tablePrefix)) {
 | 
			
		||||
            String[] searchList = StringUtils.split(tablePrefix, ",");
 | 
			
		||||
            String[] searchList = StringUtils.split(tablePrefix, StringUtils.SEPARATOR);
 | 
			
		||||
            tableName = replaceFirst(tableName, searchList);
 | 
			
		||||
        }
 | 
			
		||||
        return StringUtils.convertToCamelCase(tableName);
 | 
			
		||||
 
 | 
			
		||||
@@ -225,7 +225,7 @@ public class VelocityUtils {
 | 
			
		||||
     */
 | 
			
		||||
    public static String getDicts(GenTable genTable) {
 | 
			
		||||
        List<GenTableColumn> columns = genTable.getColumns();
 | 
			
		||||
        Set<String> dicts = new HashSet<String>();
 | 
			
		||||
        Set<String> dicts = new HashSet<>();
 | 
			
		||||
        addDicts(dicts, columns);
 | 
			
		||||
        return StringUtils.join(dicts, ", ");
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,21 +1,16 @@
 | 
			
		||||
package ${packageName}.domain.bo;
 | 
			
		||||
 | 
			
		||||
import com.ruoyi.common.mybatis.core.domain.BaseEntity;
 | 
			
		||||
import com.ruoyi.common.core.validate.AddGroup;
 | 
			
		||||
import com.ruoyi.common.core.validate.EditGroup;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.EqualsAndHashCode;
 | 
			
		||||
import jakarta.validation.constraints.*;
 | 
			
		||||
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
 | 
			
		||||
#foreach ($import in $importList)
 | 
			
		||||
import ${import};
 | 
			
		||||
#end
 | 
			
		||||
#if($table.crud || $table.sub)
 | 
			
		||||
import com.ruoyi.common.mybatis.core.domain.BaseEntity;
 | 
			
		||||
#elseif($table.tree)
 | 
			
		||||
import com.ruoyi.common.mybatis.core.domain.TreeEntity;
 | 
			
		||||
#end
 | 
			
		||||
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * ${functionName}业务对象 ${tableName}
 | 
			
		||||
@@ -23,15 +18,9 @@ import com.ruoyi.common.mybatis.core.domain.TreeEntity;
 | 
			
		||||
 * @author ${author}
 | 
			
		||||
 * @date ${datetime}
 | 
			
		||||
 */
 | 
			
		||||
#if($table.crud || $table.sub)
 | 
			
		||||
#set($Entity="BaseEntity")
 | 
			
		||||
#elseif($table.tree)
 | 
			
		||||
#set($Entity="TreeEntity<${ClassName}Bo>")
 | 
			
		||||
#end
 | 
			
		||||
 | 
			
		||||
@Data
 | 
			
		||||
@EqualsAndHashCode(callSuper = true)
 | 
			
		||||
public class ${ClassName}Bo extends ${Entity} {
 | 
			
		||||
public class ${ClassName}Bo extends BaseEntity {
 | 
			
		||||
 | 
			
		||||
#foreach ($column in $columns)
 | 
			
		||||
#if(!$table.isSuperColumn($column.javaField) && ($column.query || $column.isInsert || $column.isEdit))
 | 
			
		||||
 
 | 
			
		||||
@@ -39,7 +39,7 @@ import com.ruoyi.common.mybatis.core.page.TableDataInfo;
 | 
			
		||||
@RequestMapping("/${moduleName}/${businessName}")
 | 
			
		||||
public class ${ClassName}Controller extends BaseController {
 | 
			
		||||
 | 
			
		||||
    private final I${ClassName}Service i${ClassName}Service;
 | 
			
		||||
    private final I${ClassName}Service ${className}Service;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 查询${functionName}列表
 | 
			
		||||
@@ -52,7 +52,7 @@ public class ${ClassName}Controller extends BaseController {
 | 
			
		||||
    }
 | 
			
		||||
#elseif($table.tree)
 | 
			
		||||
    public R<List<${ClassName}Vo>> list(${ClassName}Bo bo) {
 | 
			
		||||
        List<${ClassName}Vo> list = i${ClassName}Service.queryList(bo);
 | 
			
		||||
        List<${ClassName}Vo> list = ${className}Service.queryList(bo);
 | 
			
		||||
        return R.ok(list);
 | 
			
		||||
    }
 | 
			
		||||
#end
 | 
			
		||||
@@ -64,7 +64,7 @@ public class ${ClassName}Controller extends BaseController {
 | 
			
		||||
    @Log(title = "${functionName}", businessType = BusinessType.EXPORT)
 | 
			
		||||
    @PostMapping("/export")
 | 
			
		||||
    public void export(${ClassName}Bo bo, HttpServletResponse response) {
 | 
			
		||||
        List<${ClassName}Vo> list = i${ClassName}Service.queryList(bo);
 | 
			
		||||
        List<${ClassName}Vo> list = ${className}Service.queryList(bo);
 | 
			
		||||
        ExcelUtil.exportExcel(list, "${functionName}", ${ClassName}Vo.class, response);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -77,7 +77,7 @@ public class ${ClassName}Controller extends BaseController {
 | 
			
		||||
    @GetMapping("/{${pkColumn.javaField}}")
 | 
			
		||||
    public R<${ClassName}Vo> getInfo(@NotNull(message = "主键不能为空")
 | 
			
		||||
                                     @PathVariable ${pkColumn.javaType} ${pkColumn.javaField}) {
 | 
			
		||||
        return R.ok(i${ClassName}Service.queryById(${pkColumn.javaField}));
 | 
			
		||||
        return R.ok(${className}Service.queryById(${pkColumn.javaField}));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -88,7 +88,7 @@ public class ${ClassName}Controller extends BaseController {
 | 
			
		||||
    @RepeatSubmit()
 | 
			
		||||
    @PostMapping()
 | 
			
		||||
    public R<Void> add(@Validated(AddGroup.class) @RequestBody ${ClassName}Bo bo) {
 | 
			
		||||
        return toAjax(i${ClassName}Service.insertByBo(bo));
 | 
			
		||||
        return toAjax(${className}Service.insertByBo(bo));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -99,7 +99,7 @@ public class ${ClassName}Controller extends BaseController {
 | 
			
		||||
    @RepeatSubmit()
 | 
			
		||||
    @PutMapping()
 | 
			
		||||
    public R<Void> edit(@Validated(EditGroup.class) @RequestBody ${ClassName}Bo bo) {
 | 
			
		||||
        return toAjax(i${ClassName}Service.updateByBo(bo));
 | 
			
		||||
        return toAjax(${className}Service.updateByBo(bo));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -112,6 +112,6 @@ public class ${ClassName}Controller extends BaseController {
 | 
			
		||||
    @DeleteMapping("/{${pkColumn.javaField}s}")
 | 
			
		||||
    public R<Void> remove(@NotEmpty(message = "主键不能为空")
 | 
			
		||||
                          @PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s) {
 | 
			
		||||
        return toAjax(i${ClassName}Service.deleteWithValidByIds(List.of(${pkColumn.javaField}s), true));
 | 
			
		||||
        return toAjax(${className}Service.deleteWithValidByIds(List.of(${pkColumn.javaField}s), true));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,21 +1,25 @@
 | 
			
		||||
package ${packageName}.domain;
 | 
			
		||||
 | 
			
		||||
#foreach ($column in $columns)
 | 
			
		||||
#if($column.javaField=='tenantId')
 | 
			
		||||
#set($IsTenant=1)
 | 
			
		||||
#end
 | 
			
		||||
#end
 | 
			
		||||
#if($IsTenant==1)
 | 
			
		||||
import com.ruoyi.common.tenant.core.TenantEntity;
 | 
			
		||||
#else
 | 
			
		||||
import com.ruoyi.common.mybatis.core.domain.BaseEntity;
 | 
			
		||||
#end
 | 
			
		||||
import com.baomidou.mybatisplus.annotation.*;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.EqualsAndHashCode;
 | 
			
		||||
import java.io.Serial;
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
import java.math.BigDecimal;
 | 
			
		||||
 | 
			
		||||
#foreach ($import in $importList)
 | 
			
		||||
import ${import};
 | 
			
		||||
#end
 | 
			
		||||
#if($table.crud || $table.sub)
 | 
			
		||||
import com.ruoyi.common.mybatis.core.domain.BaseEntity;
 | 
			
		||||
#elseif($table.tree)
 | 
			
		||||
import com.ruoyi.common.mybatis.core.domain.TreeEntity;
 | 
			
		||||
#end
 | 
			
		||||
 | 
			
		||||
import java.io.Serial;
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
import java.math.BigDecimal;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * ${functionName}对象 ${tableName}
 | 
			
		||||
@@ -23,10 +27,10 @@ import com.ruoyi.common.mybatis.core.domain.TreeEntity;
 | 
			
		||||
 * @author ${author}
 | 
			
		||||
 * @date ${datetime}
 | 
			
		||||
 */
 | 
			
		||||
#if($table.crud || $table.sub)
 | 
			
		||||
    #set($Entity="BaseEntity")
 | 
			
		||||
#elseif($table.tree)
 | 
			
		||||
    #set($Entity="TreeEntity<${ClassName}>")
 | 
			
		||||
#if($IsTenant==1)
 | 
			
		||||
#set($Entity="TenantEntity")
 | 
			
		||||
#else
 | 
			
		||||
#set($Entity="BaseEntity")
 | 
			
		||||
#end
 | 
			
		||||
@Data
 | 
			
		||||
@EqualsAndHashCode(callSuper = true)
 | 
			
		||||
@@ -51,6 +55,7 @@ public class ${ClassName} extends ${Entity} {
 | 
			
		||||
    @TableId(value = "$column.columnName")
 | 
			
		||||
#end
 | 
			
		||||
    private $column.javaType $column.javaField;
 | 
			
		||||
 | 
			
		||||
#end
 | 
			
		||||
#end
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -333,6 +333,7 @@ export default {
 | 
			
		||||
      // 表单校验
 | 
			
		||||
      rules: {
 | 
			
		||||
#foreach ($column in $columns)
 | 
			
		||||
#if($column.isInsert || $column.isEdit)
 | 
			
		||||
#if($column.required)
 | 
			
		||||
#set($parentheseIndex=$column.columnComment.indexOf("("))
 | 
			
		||||
#if($parentheseIndex != -1)
 | 
			
		||||
@@ -344,6 +345,7 @@ export default {
 | 
			
		||||
          { required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select" || $column.htmlType == "radio")"change"#else"blur"#end }
 | 
			
		||||
        ]#if($foreach.count != $columns.size()),#end
 | 
			
		||||
#end
 | 
			
		||||
#end
 | 
			
		||||
#end
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
@@ -404,11 +406,13 @@ export default {
 | 
			
		||||
    reset() {
 | 
			
		||||
      this.form = {
 | 
			
		||||
#foreach ($column in $columns)
 | 
			
		||||
#if($column.isInsert || $column.isEdit)
 | 
			
		||||
#if($column.htmlType == "checkbox")
 | 
			
		||||
        $column.javaField: []#if($foreach.count != $columns.size()),#end
 | 
			
		||||
#else
 | 
			
		||||
        $column.javaField: null#if($foreach.count != $columns.size()),#end
 | 
			
		||||
#end
 | 
			
		||||
#end
 | 
			
		||||
#end
 | 
			
		||||
      };
 | 
			
		||||
      this.resetForm("form");
 | 
			
		||||
 
 | 
			
		||||
@@ -343,6 +343,7 @@ export default {
 | 
			
		||||
      // 表单校验
 | 
			
		||||
      rules: {
 | 
			
		||||
#foreach ($column in $columns)
 | 
			
		||||
#if($column.isInsert || $column.isEdit)
 | 
			
		||||
#if($column.required)
 | 
			
		||||
#set($parentheseIndex=$column.columnComment.indexOf("("))
 | 
			
		||||
#if($parentheseIndex != -1)
 | 
			
		||||
@@ -354,6 +355,7 @@ export default {
 | 
			
		||||
          { required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select" || $column.htmlType == "radio")"change"#else"blur"#end }
 | 
			
		||||
        ]#if($foreach.count != $columns.size()),#end
 | 
			
		||||
#end
 | 
			
		||||
#end
 | 
			
		||||
#end
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
@@ -395,11 +397,13 @@ export default {
 | 
			
		||||
    reset() {
 | 
			
		||||
      this.form = {
 | 
			
		||||
#foreach ($column in $columns)
 | 
			
		||||
#if($column.isInsert || $column.isEdit)
 | 
			
		||||
#if($column.htmlType == "checkbox")
 | 
			
		||||
        $column.javaField: []#if($foreach.count != $columns.size()),#end
 | 
			
		||||
#else
 | 
			
		||||
        $column.javaField: undefined#if($foreach.count != $columns.size()),#end
 | 
			
		||||
#end
 | 
			
		||||
#end
 | 
			
		||||
#end
 | 
			
		||||
      };
 | 
			
		||||
      this.resetForm("form");
 | 
			
		||||
 
 | 
			
		||||
@@ -304,6 +304,7 @@ const data = reactive({
 | 
			
		||||
  },
 | 
			
		||||
  rules: {
 | 
			
		||||
#foreach ($column in $columns)
 | 
			
		||||
#if($column.isInsert || $column.isEdit)
 | 
			
		||||
#if($column.required)
 | 
			
		||||
#set($parentheseIndex=$column.columnComment.indexOf("("))
 | 
			
		||||
#if($parentheseIndex != -1)
 | 
			
		||||
@@ -315,6 +316,7 @@ const data = reactive({
 | 
			
		||||
      { required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select" || $column.htmlType == "radio")"change"#else"blur"#end }
 | 
			
		||||
    ]#if($foreach.count != $columns.size()),#end
 | 
			
		||||
#end
 | 
			
		||||
#end
 | 
			
		||||
#end
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
@@ -365,11 +367,13 @@ function cancel() {
 | 
			
		||||
function reset() {
 | 
			
		||||
  form.value = {
 | 
			
		||||
#foreach ($column in $columns)
 | 
			
		||||
#if($column.isInsert || $column.isEdit)
 | 
			
		||||
#if($column.htmlType == "checkbox")
 | 
			
		||||
    $column.javaField: []#if($foreach.count != $columns.size()),#end
 | 
			
		||||
#else
 | 
			
		||||
    $column.javaField: null#if($foreach.count != $columns.size()),#end
 | 
			
		||||
#end
 | 
			
		||||
#end
 | 
			
		||||
#end
 | 
			
		||||
  };
 | 
			
		||||
  proxy.resetForm("${businessName}Ref");
 | 
			
		||||
 
 | 
			
		||||
@@ -315,6 +315,7 @@ const data = reactive({
 | 
			
		||||
  },
 | 
			
		||||
  rules: {
 | 
			
		||||
#foreach ($column in $columns)
 | 
			
		||||
#if($column.isInsert || $column.isEdit)
 | 
			
		||||
#if($column.required)
 | 
			
		||||
#set($parentheseIndex=$column.columnComment.indexOf("("))
 | 
			
		||||
#if($parentheseIndex != -1)
 | 
			
		||||
@@ -326,6 +327,7 @@ const data = reactive({
 | 
			
		||||
      { required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select" || $column.htmlType == "radio")"change"#else"blur"#end }
 | 
			
		||||
    ]#if($foreach.count != $columns.size()),#end
 | 
			
		||||
#end
 | 
			
		||||
#end
 | 
			
		||||
#end
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
@@ -367,11 +369,13 @@ function cancel() {
 | 
			
		||||
function reset() {
 | 
			
		||||
  form.value = {
 | 
			
		||||
#foreach ($column in $columns)
 | 
			
		||||
#if($column.isInsert || $column.isEdit)
 | 
			
		||||
#if($column.htmlType == "checkbox")
 | 
			
		||||
    $column.javaField: []#if($foreach.count != $columns.size()),#end
 | 
			
		||||
#else
 | 
			
		||||
    $column.javaField: null#if($foreach.count != $columns.size()),#end
 | 
			
		||||
#end
 | 
			
		||||
#end
 | 
			
		||||
#end
 | 
			
		||||
  };
 | 
			
		||||
  proxy.resetForm("${businessName}Ref");
 | 
			
		||||
 
 | 
			
		||||
@@ -63,7 +63,7 @@
 | 
			
		||||
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>com.ruoyi</groupId>
 | 
			
		||||
            <artifactId>ruoyi-common-satoken</artifactId>
 | 
			
		||||
            <artifactId>ruoyi-common-tenant</artifactId>
 | 
			
		||||
        </dependency>
 | 
			
		||||
 | 
			
		||||
        <dependency>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,168 +0,0 @@
 | 
			
		||||
package com.ruoyi.system.controller.monitor;
 | 
			
		||||
 | 
			
		||||
import cn.dev33.satoken.annotation.SaCheckPermission;
 | 
			
		||||
import cn.hutool.core.collection.CollUtil;
 | 
			
		||||
import com.ruoyi.common.core.constant.CacheConstants;
 | 
			
		||||
import com.ruoyi.common.core.constant.CacheNames;
 | 
			
		||||
import com.ruoyi.common.core.domain.R;
 | 
			
		||||
import com.ruoyi.common.core.utils.StreamUtils;
 | 
			
		||||
import com.ruoyi.common.core.utils.StringUtils;
 | 
			
		||||
import com.ruoyi.common.json.utils.JsonUtils;
 | 
			
		||||
import com.ruoyi.common.redis.utils.CacheUtils;
 | 
			
		||||
import com.ruoyi.common.redis.utils.RedisUtils;
 | 
			
		||||
import com.ruoyi.system.domain.SysCache;
 | 
			
		||||
import com.ruoyi.system.domain.vo.CacheListInfoVo;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import org.redisson.spring.data.connection.RedissonConnectionFactory;
 | 
			
		||||
import org.springframework.data.redis.connection.RedisConnection;
 | 
			
		||||
import org.springframework.web.bind.annotation.*;
 | 
			
		||||
 | 
			
		||||
import java.util.*;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 缓存监控
 | 
			
		||||
 *
 | 
			
		||||
 * @author Lion Li
 | 
			
		||||
 */
 | 
			
		||||
@RequiredArgsConstructor
 | 
			
		||||
@RestController
 | 
			
		||||
@RequestMapping("/monitor/cache")
 | 
			
		||||
public class CacheController {
 | 
			
		||||
 | 
			
		||||
    private final RedissonConnectionFactory connectionFactory;
 | 
			
		||||
 | 
			
		||||
    private final static List<SysCache> CACHES = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
    static {
 | 
			
		||||
        CACHES.add(new SysCache(CacheConstants.LOGIN_TOKEN_KEY, "用户信息"));
 | 
			
		||||
        CACHES.add(new SysCache(CacheConstants.ONLINE_TOKEN_KEY, "在线用户"));
 | 
			
		||||
        CACHES.add(new SysCache(CacheNames.SYS_CONFIG, "配置信息"));
 | 
			
		||||
        CACHES.add(new SysCache(CacheNames.SYS_DICT, "数据字典"));
 | 
			
		||||
        CACHES.add(new SysCache(CacheConstants.CAPTCHA_CODE_KEY, "验证码"));
 | 
			
		||||
        CACHES.add(new SysCache(CacheConstants.REPEAT_SUBMIT_KEY, "防重提交"));
 | 
			
		||||
        CACHES.add(new SysCache(CacheConstants.RATE_LIMIT_KEY, "限流处理"));
 | 
			
		||||
        CACHES.add(new SysCache(CacheNames.SYS_OSS_CONFIG, "OSS配置"));
 | 
			
		||||
        CACHES.add(new SysCache(CacheConstants.PWD_ERR_CNT_KEY, "密码错误次数"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取缓存监控列表
 | 
			
		||||
     */
 | 
			
		||||
    @SaCheckPermission("monitor:cache:list")
 | 
			
		||||
    @GetMapping()
 | 
			
		||||
    public R<CacheListInfoVo> getInfo() throws Exception {
 | 
			
		||||
        RedisConnection connection = connectionFactory.getConnection();
 | 
			
		||||
        Properties commandStats = connection.commands().info("commandstats");
 | 
			
		||||
 | 
			
		||||
        List<Map<String, String>> pieList = new ArrayList<>();
 | 
			
		||||
        if (commandStats != null) {
 | 
			
		||||
            commandStats.stringPropertyNames().forEach(key -> {
 | 
			
		||||
                Map<String, String> data = new HashMap<>(2);
 | 
			
		||||
                String property = commandStats.getProperty(key);
 | 
			
		||||
                data.put("name", StringUtils.removeStart(key, "cmdstat_"));
 | 
			
		||||
                data.put("value", StringUtils.substringBetween(property, "calls=", ",usec"));
 | 
			
		||||
                pieList.add(data);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        CacheListInfoVo infoVo = new CacheListInfoVo();
 | 
			
		||||
        infoVo.setInfo(connection.commands().info());
 | 
			
		||||
        infoVo.setDbSize(connection.commands().dbSize());
 | 
			
		||||
        infoVo.setCommandStats(pieList);
 | 
			
		||||
        return R.ok(infoVo);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取缓存监控缓存名列表
 | 
			
		||||
     */
 | 
			
		||||
    @SaCheckPermission("monitor:cache:list")
 | 
			
		||||
    @GetMapping("/getNames")
 | 
			
		||||
    public R<List<SysCache>> cache() {
 | 
			
		||||
        return R.ok(CACHES);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取缓存监控Key列表
 | 
			
		||||
     *
 | 
			
		||||
     * @param cacheName 缓存名
 | 
			
		||||
     */
 | 
			
		||||
    @SaCheckPermission("monitor:cache:list")
 | 
			
		||||
    @GetMapping("/getKeys/{cacheName}")
 | 
			
		||||
    public R<Collection<String>> getCacheKeys(@PathVariable String cacheName) {
 | 
			
		||||
        Collection<String> cacheKeys = new HashSet<>(0);
 | 
			
		||||
        if (isCacheNames(cacheName)) {
 | 
			
		||||
            Set<Object> keys = CacheUtils.keys(cacheName);
 | 
			
		||||
            if (CollUtil.isNotEmpty(keys)) {
 | 
			
		||||
                cacheKeys = StreamUtils.toList(keys, Object::toString);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            cacheKeys = RedisUtils.keys(cacheName + "*");
 | 
			
		||||
        }
 | 
			
		||||
        return R.ok(cacheKeys);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取缓存监控缓存值详情
 | 
			
		||||
     *
 | 
			
		||||
     * @param cacheName 缓存名
 | 
			
		||||
     * @param cacheKey  缓存key
 | 
			
		||||
     */
 | 
			
		||||
    @SaCheckPermission("monitor:cache:list")
 | 
			
		||||
    @GetMapping("/getValue/{cacheName}/{cacheKey}")
 | 
			
		||||
    public R<SysCache> getCacheValue(@PathVariable String cacheName, @PathVariable String cacheKey) {
 | 
			
		||||
        Object cacheValue;
 | 
			
		||||
        if (isCacheNames(cacheName)) {
 | 
			
		||||
            cacheValue = CacheUtils.get(cacheName, cacheKey);
 | 
			
		||||
        } else {
 | 
			
		||||
            cacheValue = RedisUtils.getCacheObject(cacheKey);
 | 
			
		||||
        }
 | 
			
		||||
        SysCache sysCache = new SysCache(cacheName, cacheKey, JsonUtils.toJsonString(cacheValue));
 | 
			
		||||
        return R.ok(sysCache);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 清理缓存监控缓存名
 | 
			
		||||
     *
 | 
			
		||||
     * @param cacheName 缓存名
 | 
			
		||||
     */
 | 
			
		||||
    @SaCheckPermission("monitor:cache:list")
 | 
			
		||||
    @DeleteMapping("/clearCacheName/{cacheName}")
 | 
			
		||||
    public R<Void> clearCacheName(@PathVariable String cacheName) {
 | 
			
		||||
        if (isCacheNames(cacheName)) {
 | 
			
		||||
            CacheUtils.clear(cacheName);
 | 
			
		||||
        } else {
 | 
			
		||||
            RedisUtils.deleteKeys(cacheName + "*");
 | 
			
		||||
        }
 | 
			
		||||
        return R.ok();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 清理缓存监控Key
 | 
			
		||||
     *
 | 
			
		||||
     * @param cacheKey key名
 | 
			
		||||
     */
 | 
			
		||||
    @SaCheckPermission("monitor:cache:list")
 | 
			
		||||
    @DeleteMapping("/clearCacheKey/{cacheName}/{cacheKey}")
 | 
			
		||||
    public R<Void> clearCacheKey(@PathVariable String cacheName, @PathVariable String cacheKey) {
 | 
			
		||||
        if (isCacheNames(cacheName)) {
 | 
			
		||||
            CacheUtils.evict(cacheName, cacheKey);
 | 
			
		||||
        } else {
 | 
			
		||||
            RedisUtils.deleteObject(cacheKey);
 | 
			
		||||
        }
 | 
			
		||||
        return R.ok();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 清理全部缓存监控
 | 
			
		||||
     */
 | 
			
		||||
    @SaCheckPermission("monitor:cache:list")
 | 
			
		||||
    @DeleteMapping("/clearCacheAll")
 | 
			
		||||
    public R<Void> clearCacheAll() {
 | 
			
		||||
        RedisUtils.deleteKeys("*");
 | 
			
		||||
        return R.ok();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private boolean isCacheNames(String cacheName) {
 | 
			
		||||
        return !StringUtils.contains(cacheName, ":");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,22 +1,23 @@
 | 
			
		||||
package com.ruoyi.system.controller.monitor;
 | 
			
		||||
 | 
			
		||||
import cn.dev33.satoken.annotation.SaCheckPermission;
 | 
			
		||||
import com.ruoyi.common.core.constant.CacheConstants;
 | 
			
		||||
import com.ruoyi.common.mybatis.core.page.PageQuery;
 | 
			
		||||
import com.ruoyi.common.core.constant.GlobalConstants;
 | 
			
		||||
import com.ruoyi.common.core.domain.R;
 | 
			
		||||
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
 | 
			
		||||
import com.ruoyi.common.web.core.BaseController;
 | 
			
		||||
import com.ruoyi.common.excel.utils.ExcelUtil;
 | 
			
		||||
import com.ruoyi.common.log.annotation.Log;
 | 
			
		||||
import com.ruoyi.common.log.enums.BusinessType;
 | 
			
		||||
import com.ruoyi.common.mybatis.core.page.PageQuery;
 | 
			
		||||
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
 | 
			
		||||
import com.ruoyi.common.redis.utils.RedisUtils;
 | 
			
		||||
import com.ruoyi.system.domain.SysLogininfor;
 | 
			
		||||
import com.ruoyi.common.web.core.BaseController;
 | 
			
		||||
import com.ruoyi.system.domain.bo.SysLogininforBo;
 | 
			
		||||
import com.ruoyi.system.domain.vo.SysLogininforVo;
 | 
			
		||||
import com.ruoyi.system.service.ISysLogininforService;
 | 
			
		||||
import jakarta.servlet.http.HttpServletResponse;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import org.springframework.validation.annotation.Validated;
 | 
			
		||||
import org.springframework.web.bind.annotation.*;
 | 
			
		||||
 | 
			
		||||
import jakarta.servlet.http.HttpServletResponse;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -37,7 +38,7 @@ public class SysLogininforController extends BaseController {
 | 
			
		||||
     */
 | 
			
		||||
    @SaCheckPermission("monitor:logininfor:list")
 | 
			
		||||
    @GetMapping("/list")
 | 
			
		||||
    public TableDataInfo<SysLogininfor> list(SysLogininfor logininfor, PageQuery pageQuery) {
 | 
			
		||||
    public TableDataInfo<SysLogininforVo> list(SysLogininforBo logininfor, PageQuery pageQuery) {
 | 
			
		||||
        return logininforService.selectPageLogininforList(logininfor, pageQuery);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -47,9 +48,9 @@ public class SysLogininforController extends BaseController {
 | 
			
		||||
    @Log(title = "登录日志", businessType = BusinessType.EXPORT)
 | 
			
		||||
    @SaCheckPermission("monitor:logininfor:export")
 | 
			
		||||
    @PostMapping("/export")
 | 
			
		||||
    public void export(SysLogininfor logininfor, HttpServletResponse response) {
 | 
			
		||||
        List<SysLogininfor> list = logininforService.selectLogininforList(logininfor);
 | 
			
		||||
        ExcelUtil.exportExcel(list, "登录日志", SysLogininfor.class, response);
 | 
			
		||||
    public void export(SysLogininforBo logininfor, HttpServletResponse response) {
 | 
			
		||||
        List<SysLogininforVo> list = logininforService.selectLogininforList(logininfor);
 | 
			
		||||
        ExcelUtil.exportExcel(list, "登录日志", SysLogininforVo.class, response);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -78,7 +79,7 @@ public class SysLogininforController extends BaseController {
 | 
			
		||||
    @Log(title = "账户解锁", businessType = BusinessType.OTHER)
 | 
			
		||||
    @GetMapping("/unlock/{userName}")
 | 
			
		||||
    public R<Void> unlock(@PathVariable("userName") String userName) {
 | 
			
		||||
        String loginName = CacheConstants.PWD_ERR_CNT_KEY + userName;
 | 
			
		||||
        String loginName = GlobalConstants.PWD_ERR_CNT_KEY + userName;
 | 
			
		||||
        if (RedisUtils.hasKey(loginName)) {
 | 
			
		||||
            RedisUtils.deleteObject(loginName);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,8 @@ import com.ruoyi.common.core.domain.R;
 | 
			
		||||
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
 | 
			
		||||
import com.ruoyi.common.log.enums.BusinessType;
 | 
			
		||||
import com.ruoyi.common.excel.utils.ExcelUtil;
 | 
			
		||||
import com.ruoyi.system.domain.SysOperLog;
 | 
			
		||||
import com.ruoyi.system.domain.bo.SysOperLogBo;
 | 
			
		||||
import com.ruoyi.system.domain.vo.SysOperLogVo;
 | 
			
		||||
import com.ruoyi.system.service.ISysOperLogService;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import org.springframework.validation.annotation.Validated;
 | 
			
		||||
@@ -35,7 +36,7 @@ public class SysOperlogController extends BaseController {
 | 
			
		||||
     */
 | 
			
		||||
    @SaCheckPermission("monitor:operlog:list")
 | 
			
		||||
    @GetMapping("/list")
 | 
			
		||||
    public TableDataInfo<SysOperLog> list(SysOperLog operLog, PageQuery pageQuery) {
 | 
			
		||||
    public TableDataInfo<SysOperLogVo> list(SysOperLogBo operLog, PageQuery pageQuery) {
 | 
			
		||||
        return operLogService.selectPageOperLogList(operLog, pageQuery);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -45,9 +46,9 @@ public class SysOperlogController extends BaseController {
 | 
			
		||||
    @Log(title = "操作日志", businessType = BusinessType.EXPORT)
 | 
			
		||||
    @SaCheckPermission("monitor:operlog:export")
 | 
			
		||||
    @PostMapping("/export")
 | 
			
		||||
    public void export(SysOperLog operLog, HttpServletResponse response) {
 | 
			
		||||
        List<SysOperLog> list = operLogService.selectOperLogList(operLog);
 | 
			
		||||
        ExcelUtil.exportExcel(list, "操作日志", SysOperLog.class, response);
 | 
			
		||||
    public void export(SysOperLogBo operLog, HttpServletResponse response) {
 | 
			
		||||
        List<SysOperLogVo> list = operLogService.selectOperLogList(operLog);
 | 
			
		||||
        ExcelUtil.exportExcel(list, "操作日志", SysOperLogVo.class, response);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -4,16 +4,17 @@ import cn.dev33.satoken.annotation.SaCheckPermission;
 | 
			
		||||
import cn.dev33.satoken.exception.NotLoginException;
 | 
			
		||||
import cn.dev33.satoken.stp.StpUtil;
 | 
			
		||||
import cn.hutool.core.bean.BeanUtil;
 | 
			
		||||
import com.ruoyi.common.log.annotation.Log;
 | 
			
		||||
import com.ruoyi.common.core.constant.CacheConstants;
 | 
			
		||||
import com.ruoyi.common.web.core.BaseController;
 | 
			
		||||
import com.ruoyi.common.core.constant.GlobalConstants;
 | 
			
		||||
import com.ruoyi.common.core.domain.R;
 | 
			
		||||
import com.ruoyi.common.core.domain.dto.UserOnlineDTO;
 | 
			
		||||
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
 | 
			
		||||
import com.ruoyi.common.log.enums.BusinessType;
 | 
			
		||||
import com.ruoyi.common.core.utils.StreamUtils;
 | 
			
		||||
import com.ruoyi.common.core.utils.StringUtils;
 | 
			
		||||
import com.ruoyi.common.log.annotation.Log;
 | 
			
		||||
import com.ruoyi.common.log.enums.BusinessType;
 | 
			
		||||
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
 | 
			
		||||
import com.ruoyi.common.redis.utils.RedisUtils;
 | 
			
		||||
import com.ruoyi.common.web.core.BaseController;
 | 
			
		||||
import com.ruoyi.system.domain.SysUserOnline;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import org.springframework.web.bind.annotation.*;
 | 
			
		||||
@@ -45,7 +46,7 @@ public class SysUserOnlineController extends BaseController {
 | 
			
		||||
        List<String> keys = StpUtil.searchTokenValue("", 0, -1, false);
 | 
			
		||||
        List<UserOnlineDTO> userOnlineDTOList = new ArrayList<>();
 | 
			
		||||
        for (String key : keys) {
 | 
			
		||||
            String token = key.replace(CacheConstants.LOGIN_TOKEN_KEY, "");
 | 
			
		||||
            String token = key.replace(GlobalConstants.LOGIN_TOKEN_KEY, "");
 | 
			
		||||
            // 如果已经过期则跳过
 | 
			
		||||
            if (StpUtil.stpLogic.getTokenActivityTimeoutByToken(token) < -1) {
 | 
			
		||||
                continue;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,13 @@
 | 
			
		||||
package com.ruoyi.system.controller.system;
 | 
			
		||||
 | 
			
		||||
import cn.dev33.satoken.annotation.SaCheckPermission;
 | 
			
		||||
import cn.hutool.core.util.ArrayUtil;
 | 
			
		||||
import com.ruoyi.common.log.annotation.Log;
 | 
			
		||||
import cn.hutool.core.convert.Convert;
 | 
			
		||||
import com.ruoyi.common.core.constant.UserConstants;
 | 
			
		||||
import com.ruoyi.common.web.core.BaseController;
 | 
			
		||||
import com.ruoyi.common.core.domain.R;
 | 
			
		||||
import com.ruoyi.common.log.enums.BusinessType;
 | 
			
		||||
import com.ruoyi.common.core.utils.StringUtils;
 | 
			
		||||
import com.ruoyi.common.log.annotation.Log;
 | 
			
		||||
import com.ruoyi.common.log.enums.BusinessType;
 | 
			
		||||
import com.ruoyi.common.web.core.BaseController;
 | 
			
		||||
import com.ruoyi.system.domain.bo.SysDeptBo;
 | 
			
		||||
import com.ruoyi.system.domain.vo.SysDeptVo;
 | 
			
		||||
import com.ruoyi.system.service.ISysDeptService;
 | 
			
		||||
@@ -50,7 +50,7 @@ public class SysDeptController extends BaseController {
 | 
			
		||||
    public R<List<SysDeptVo>> excludeChild(@PathVariable(value = "deptId", required = false) Long deptId) {
 | 
			
		||||
        List<SysDeptVo> depts = deptService.selectDeptList(new SysDeptBo());
 | 
			
		||||
        depts.removeIf(d -> d.getDeptId().equals(deptId)
 | 
			
		||||
            || ArrayUtil.contains(StringUtils.split(d.getAncestors(), ","), deptId + ""));
 | 
			
		||||
            || StringUtils.splitList(d.getAncestors()).contains(Convert.toStr(deptId)));
 | 
			
		||||
        return R.ok(depts);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,10 @@
 | 
			
		||||
package com.ruoyi.system.controller.system;
 | 
			
		||||
 | 
			
		||||
import cn.dev33.satoken.annotation.SaCheckPermission;
 | 
			
		||||
import cn.dev33.satoken.annotation.SaCheckRole;
 | 
			
		||||
import cn.dev33.satoken.annotation.SaMode;
 | 
			
		||||
import cn.hutool.core.lang.tree.Tree;
 | 
			
		||||
import com.ruoyi.common.core.constant.TenantConstants;
 | 
			
		||||
import com.ruoyi.common.core.constant.UserConstants;
 | 
			
		||||
import com.ruoyi.common.core.domain.R;
 | 
			
		||||
import com.ruoyi.common.core.utils.StringUtils;
 | 
			
		||||
@@ -35,6 +38,10 @@ public class SysMenuController extends BaseController {
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取菜单列表
 | 
			
		||||
     */
 | 
			
		||||
    @SaCheckRole(value = {
 | 
			
		||||
            TenantConstants.SUPER_ADMIN_ROLE_KEY,
 | 
			
		||||
            TenantConstants.TENANT_ADMIN_ROLE_KEY
 | 
			
		||||
    }, mode = SaMode.OR)
 | 
			
		||||
    @SaCheckPermission("system:menu:list")
 | 
			
		||||
    @GetMapping("/list")
 | 
			
		||||
    public R<List<SysMenuVo>> list(SysMenuBo menu) {
 | 
			
		||||
@@ -47,6 +54,10 @@ public class SysMenuController extends BaseController {
 | 
			
		||||
     *
 | 
			
		||||
     * @param menuId 菜单ID
 | 
			
		||||
     */
 | 
			
		||||
    @SaCheckRole(value = {
 | 
			
		||||
            TenantConstants.SUPER_ADMIN_ROLE_KEY,
 | 
			
		||||
            TenantConstants.TENANT_ADMIN_ROLE_KEY
 | 
			
		||||
    }, mode = SaMode.OR)
 | 
			
		||||
    @SaCheckPermission("system:menu:query")
 | 
			
		||||
    @GetMapping(value = "/{menuId}")
 | 
			
		||||
    public R<SysMenuVo> getInfo(@PathVariable Long menuId) {
 | 
			
		||||
@@ -56,6 +67,11 @@ public class SysMenuController extends BaseController {
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取菜单下拉树列表
 | 
			
		||||
     */
 | 
			
		||||
    @SaCheckRole(value = {
 | 
			
		||||
            TenantConstants.SUPER_ADMIN_ROLE_KEY,
 | 
			
		||||
            TenantConstants.TENANT_ADMIN_ROLE_KEY
 | 
			
		||||
    }, mode = SaMode.OR)
 | 
			
		||||
    @SaCheckPermission("system:menu:query")
 | 
			
		||||
    @GetMapping("/treeselect")
 | 
			
		||||
    public R<List<Tree<Long>>> treeselect(SysMenuBo menu) {
 | 
			
		||||
        List<SysMenuVo> menus = menuService.selectMenuList(menu, LoginHelper.getUserId());
 | 
			
		||||
@@ -67,6 +83,11 @@ public class SysMenuController extends BaseController {
 | 
			
		||||
     *
 | 
			
		||||
     * @param roleId 角色ID
 | 
			
		||||
     */
 | 
			
		||||
    @SaCheckRole(value = {
 | 
			
		||||
            TenantConstants.SUPER_ADMIN_ROLE_KEY,
 | 
			
		||||
            TenantConstants.TENANT_ADMIN_ROLE_KEY
 | 
			
		||||
    }, mode = SaMode.OR)
 | 
			
		||||
    @SaCheckPermission("system:menu:query")
 | 
			
		||||
    @GetMapping(value = "/roleMenuTreeselect/{roleId}")
 | 
			
		||||
    public R<MenuTreeSelectVo> roleMenuTreeselect(@PathVariable("roleId") Long roleId) {
 | 
			
		||||
        List<SysMenuVo> menus = menuService.selectMenuList(LoginHelper.getUserId());
 | 
			
		||||
@@ -76,9 +97,26 @@ public class SysMenuController extends BaseController {
 | 
			
		||||
        return R.ok(selectVo);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 加载对应租户套餐菜单列表树
 | 
			
		||||
     *
 | 
			
		||||
     * @param packageId 租户套餐ID
 | 
			
		||||
     */
 | 
			
		||||
    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
 | 
			
		||||
    @SaCheckPermission("system:menu:query")
 | 
			
		||||
    @GetMapping(value = "/tenantPackageMenuTreeselect/{packageId}")
 | 
			
		||||
    public R<MenuTreeSelectVo> tenantPackageMenuTreeselect(@PathVariable("packageId") Long packageId) {
 | 
			
		||||
        List<SysMenuVo> menus = menuService.selectMenuList(LoginHelper.getUserId());
 | 
			
		||||
        MenuTreeSelectVo selectVo = new MenuTreeSelectVo();
 | 
			
		||||
        selectVo.setCheckedKeys(menuService.selectMenuListByPackageId(packageId));
 | 
			
		||||
        selectVo.setMenus(menuService.buildMenuTreeSelect(menus));
 | 
			
		||||
        return R.ok(selectVo);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 新增菜单
 | 
			
		||||
     */
 | 
			
		||||
    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
 | 
			
		||||
    @SaCheckPermission("system:menu:add")
 | 
			
		||||
    @Log(title = "菜单管理", businessType = BusinessType.INSERT)
 | 
			
		||||
    @PostMapping
 | 
			
		||||
@@ -94,6 +132,7 @@ public class SysMenuController extends BaseController {
 | 
			
		||||
    /**
 | 
			
		||||
     * 修改菜单
 | 
			
		||||
     */
 | 
			
		||||
    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
 | 
			
		||||
    @SaCheckPermission("system:menu:edit")
 | 
			
		||||
    @Log(title = "菜单管理", businessType = BusinessType.UPDATE)
 | 
			
		||||
    @PutMapping
 | 
			
		||||
@@ -113,6 +152,7 @@ public class SysMenuController extends BaseController {
 | 
			
		||||
     *
 | 
			
		||||
     * @param menuId 菜单ID
 | 
			
		||||
     */
 | 
			
		||||
    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
 | 
			
		||||
    @SaCheckPermission("system:menu:remove")
 | 
			
		||||
    @Log(title = "菜单管理", businessType = BusinessType.DELETE)
 | 
			
		||||
    @DeleteMapping("/{menuId}")
 | 
			
		||||
@@ -125,4 +165,5 @@ public class SysMenuController extends BaseController {
 | 
			
		||||
        }
 | 
			
		||||
        return toAjax(menuService.deleteMenuById(menuId));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -35,7 +35,7 @@ import java.util.List;
 | 
			
		||||
@RequestMapping("/system/oss/config")
 | 
			
		||||
public class SysOssConfigController extends BaseController {
 | 
			
		||||
 | 
			
		||||
    private final ISysOssConfigService iSysOssConfigService;
 | 
			
		||||
    private final ISysOssConfigService sysOssConfigService;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 查询对象存储配置列表
 | 
			
		||||
@@ -43,7 +43,7 @@ public class SysOssConfigController extends BaseController {
 | 
			
		||||
    @SaCheckPermission("system:oss:list")
 | 
			
		||||
    @GetMapping("/list")
 | 
			
		||||
    public TableDataInfo<SysOssConfigVo> list(@Validated(QueryGroup.class) SysOssConfigBo bo, PageQuery pageQuery) {
 | 
			
		||||
        return iSysOssConfigService.queryPageList(bo, pageQuery);
 | 
			
		||||
        return sysOssConfigService.queryPageList(bo, pageQuery);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -55,7 +55,7 @@ public class SysOssConfigController extends BaseController {
 | 
			
		||||
    @GetMapping("/{ossConfigId}")
 | 
			
		||||
    public R<SysOssConfigVo> getInfo(@NotNull(message = "主键不能为空")
 | 
			
		||||
                                     @PathVariable Long ossConfigId) {
 | 
			
		||||
        return R.ok(iSysOssConfigService.queryById(ossConfigId));
 | 
			
		||||
        return R.ok(sysOssConfigService.queryById(ossConfigId));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -66,7 +66,7 @@ public class SysOssConfigController extends BaseController {
 | 
			
		||||
    @RepeatSubmit()
 | 
			
		||||
    @PostMapping()
 | 
			
		||||
    public R<Void> add(@Validated(AddGroup.class) @RequestBody SysOssConfigBo bo) {
 | 
			
		||||
        return toAjax(iSysOssConfigService.insertByBo(bo));
 | 
			
		||||
        return toAjax(sysOssConfigService.insertByBo(bo));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -77,7 +77,7 @@ public class SysOssConfigController extends BaseController {
 | 
			
		||||
    @RepeatSubmit()
 | 
			
		||||
    @PutMapping()
 | 
			
		||||
    public R<Void> edit(@Validated(EditGroup.class) @RequestBody SysOssConfigBo bo) {
 | 
			
		||||
        return toAjax(iSysOssConfigService.updateByBo(bo));
 | 
			
		||||
        return toAjax(sysOssConfigService.updateByBo(bo));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -90,7 +90,7 @@ public class SysOssConfigController extends BaseController {
 | 
			
		||||
    @DeleteMapping("/{ossConfigIds}")
 | 
			
		||||
    public R<Void> remove(@NotEmpty(message = "主键不能为空")
 | 
			
		||||
                          @PathVariable Long[] ossConfigIds) {
 | 
			
		||||
        return toAjax(iSysOssConfigService.deleteWithValidByIds(List.of(ossConfigIds), true));
 | 
			
		||||
        return toAjax(sysOssConfigService.deleteWithValidByIds(List.of(ossConfigIds), true));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -100,6 +100,6 @@ public class SysOssConfigController extends BaseController {
 | 
			
		||||
    @Log(title = "对象存储状态修改", businessType = BusinessType.UPDATE)
 | 
			
		||||
    @PutMapping("/changeStatus")
 | 
			
		||||
    public R<Void> changeStatus(@RequestBody SysOssConfigBo bo) {
 | 
			
		||||
        return toAjax(iSysOssConfigService.updateOssConfigStatus(bo));
 | 
			
		||||
        return toAjax(sysOssConfigService.updateOssConfigStatus(bo));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -38,7 +38,7 @@ import java.util.List;
 | 
			
		||||
@RequestMapping("/system/oss")
 | 
			
		||||
public class SysOssController extends BaseController {
 | 
			
		||||
 | 
			
		||||
    private final ISysOssService iSysOssService;
 | 
			
		||||
    private final ISysOssService sysOssService;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 查询OSS对象存储列表
 | 
			
		||||
@@ -46,7 +46,7 @@ public class SysOssController extends BaseController {
 | 
			
		||||
    @SaCheckPermission("system:oss:list")
 | 
			
		||||
    @GetMapping("/list")
 | 
			
		||||
    public TableDataInfo<SysOssVo> list(@Validated(QueryGroup.class) SysOssBo bo, PageQuery pageQuery) {
 | 
			
		||||
        return iSysOssService.queryPageList(bo, pageQuery);
 | 
			
		||||
        return sysOssService.queryPageList(bo, pageQuery);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -58,7 +58,7 @@ public class SysOssController extends BaseController {
 | 
			
		||||
    @GetMapping("/listByIds/{ossIds}")
 | 
			
		||||
    public R<List<SysOssVo>> listByIds(@NotEmpty(message = "主键不能为空")
 | 
			
		||||
                                       @PathVariable Long[] ossIds) {
 | 
			
		||||
        List<SysOssVo> list = iSysOssService.listByIds(Arrays.asList(ossIds));
 | 
			
		||||
        List<SysOssVo> list = sysOssService.listByIds(Arrays.asList(ossIds));
 | 
			
		||||
        return R.ok(list);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -74,7 +74,7 @@ public class SysOssController extends BaseController {
 | 
			
		||||
        if (ObjectUtil.isNull(file)) {
 | 
			
		||||
            throw new ServiceException("上传文件不能为空");
 | 
			
		||||
        }
 | 
			
		||||
        SysOssVo oss = iSysOssService.upload(file);
 | 
			
		||||
        SysOssVo oss = sysOssService.upload(file);
 | 
			
		||||
        SysOssUploadVo uploadVo = new SysOssUploadVo();
 | 
			
		||||
        uploadVo.setUrl(oss.getUrl());
 | 
			
		||||
        uploadVo.setFileName(oss.getOriginalName());
 | 
			
		||||
@@ -90,7 +90,7 @@ public class SysOssController extends BaseController {
 | 
			
		||||
    @SaCheckPermission("system:oss:download")
 | 
			
		||||
    @GetMapping("/download/{ossId}")
 | 
			
		||||
    public void download(@PathVariable Long ossId, HttpServletResponse response) throws IOException {
 | 
			
		||||
        iSysOssService.download(ossId, response);
 | 
			
		||||
        sysOssService.download(ossId, response);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -103,7 +103,7 @@ public class SysOssController extends BaseController {
 | 
			
		||||
    @DeleteMapping("/{ossIds}")
 | 
			
		||||
    public R<Void> remove(@NotEmpty(message = "主键不能为空")
 | 
			
		||||
                          @PathVariable Long[] ossIds) {
 | 
			
		||||
        return toAjax(iSysOssService.deleteWithValidByIds(List.of(ossIds), true));
 | 
			
		||||
        return toAjax(sysOssService.deleteWithValidByIds(List.of(ossIds), true));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -39,7 +39,7 @@ import java.util.Arrays;
 | 
			
		||||
public class SysProfileController extends BaseController {
 | 
			
		||||
 | 
			
		||||
    private final ISysUserService userService;
 | 
			
		||||
    private final ISysOssService iSysOssService;
 | 
			
		||||
    private final ISysOssService sysOssService;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 个人信息
 | 
			
		||||
@@ -114,7 +114,7 @@ public class SysProfileController extends BaseController {
 | 
			
		||||
            if (!StringUtils.equalsAnyIgnoreCase(extension, MimeTypeUtils.IMAGE_EXTENSION)) {
 | 
			
		||||
                return R.fail("文件格式不正确,请上传" + Arrays.toString(MimeTypeUtils.IMAGE_EXTENSION) + "格式");
 | 
			
		||||
            }
 | 
			
		||||
            SysOssVo oss = iSysOssService.upload(avatarfile);
 | 
			
		||||
            SysOssVo oss = sysOssService.upload(avatarfile);
 | 
			
		||||
            String avatar = oss.getUrl();
 | 
			
		||||
            if (userService.updateUserAvatar(LoginHelper.getUsername(), oss.getOssId())) {
 | 
			
		||||
                AvatarVo avatarVo = new AvatarVo();
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,13 @@
 | 
			
		||||
package com.ruoyi.system.controller.system;
 | 
			
		||||
 | 
			
		||||
import cn.dev33.satoken.annotation.SaCheckPermission;
 | 
			
		||||
import cn.hutool.core.util.ObjectUtil;
 | 
			
		||||
import com.ruoyi.common.core.constant.UserConstants;
 | 
			
		||||
import com.ruoyi.common.core.domain.R;
 | 
			
		||||
import com.ruoyi.common.core.domain.model.LoginUser;
 | 
			
		||||
import com.ruoyi.common.excel.utils.ExcelUtil;
 | 
			
		||||
import com.ruoyi.common.log.annotation.Log;
 | 
			
		||||
import com.ruoyi.common.log.enums.BusinessType;
 | 
			
		||||
import com.ruoyi.common.mybatis.core.page.PageQuery;
 | 
			
		||||
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
 | 
			
		||||
import com.ruoyi.common.satoken.utils.LoginHelper;
 | 
			
		||||
import com.ruoyi.common.web.core.BaseController;
 | 
			
		||||
import com.ruoyi.system.domain.SysDept;
 | 
			
		||||
import com.ruoyi.system.domain.SysUserRole;
 | 
			
		||||
@@ -20,9 +17,9 @@ import com.ruoyi.system.domain.vo.DeptTreeSelectVo;
 | 
			
		||||
import com.ruoyi.system.domain.vo.SysRoleVo;
 | 
			
		||||
import com.ruoyi.system.domain.vo.SysUserVo;
 | 
			
		||||
import com.ruoyi.system.service.ISysDeptService;
 | 
			
		||||
import com.ruoyi.system.service.ISysPermissionService;
 | 
			
		||||
import com.ruoyi.system.service.ISysRoleService;
 | 
			
		||||
import com.ruoyi.system.service.ISysUserService;
 | 
			
		||||
import com.ruoyi.system.service.SysPermissionService;
 | 
			
		||||
import jakarta.servlet.http.HttpServletResponse;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import org.springframework.validation.annotation.Validated;
 | 
			
		||||
@@ -44,7 +41,7 @@ public class SysRoleController extends BaseController {
 | 
			
		||||
    private final ISysRoleService roleService;
 | 
			
		||||
    private final ISysUserService userService;
 | 
			
		||||
    private final ISysDeptService deptService;
 | 
			
		||||
    private final SysPermissionService permissionService;
 | 
			
		||||
    private final ISysPermissionService permissionService;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取角色信息列表
 | 
			
		||||
@@ -110,13 +107,14 @@ public class SysRoleController extends BaseController {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (roleService.updateRole(role) > 0) {
 | 
			
		||||
            // 更新缓存用户权限
 | 
			
		||||
            LoginUser loginUser = LoginHelper.getLoginUser();
 | 
			
		||||
            SysUserVo sysUser = userService.selectUserById(loginUser.getUserId());
 | 
			
		||||
            if (ObjectUtil.isNotNull(sysUser) && !LoginHelper.isAdmin()) {
 | 
			
		||||
                loginUser.setMenuPermission(permissionService.getMenuPermission(sysUser.getUserId(), sysUser.isAdmin()));
 | 
			
		||||
                LoginHelper.setLoginUser(loginUser);
 | 
			
		||||
            }
 | 
			
		||||
//            // 更新缓存用户权限
 | 
			
		||||
//            LoginUser loginUser = LoginHelper.getLoginUser();
 | 
			
		||||
//            SysUserVo sysUser = userService.selectUserById(loginUser.getUserId());
 | 
			
		||||
//            if (ObjectUtil.isNotNull(sysUser)) {
 | 
			
		||||
//                loginUser.setMenuPermission(permissionService.getMenuPermission(sysUser.getUserId()));
 | 
			
		||||
//                LoginHelper.setLoginUser(loginUser);
 | 
			
		||||
//            }
 | 
			
		||||
            // todo LoginUser 改为存储到token内部 无法热更新 等待后续想办法
 | 
			
		||||
            return R.ok();
 | 
			
		||||
        }
 | 
			
		||||
        return R.fail("修改角色'" + role.getRoleName() + "'失败,请联系管理员");
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,168 @@
 | 
			
		||||
package com.ruoyi.system.controller.system;
 | 
			
		||||
 | 
			
		||||
import cn.dev33.satoken.annotation.SaCheckPermission;
 | 
			
		||||
import cn.dev33.satoken.annotation.SaCheckRole;
 | 
			
		||||
import com.baomidou.lock.annotation.Lock4j;
 | 
			
		||||
import com.ruoyi.common.core.constant.TenantConstants;
 | 
			
		||||
import com.ruoyi.common.core.constant.UserConstants;
 | 
			
		||||
import com.ruoyi.common.core.domain.R;
 | 
			
		||||
import com.ruoyi.common.core.exception.ServiceException;
 | 
			
		||||
import com.ruoyi.common.core.validate.AddGroup;
 | 
			
		||||
import com.ruoyi.common.core.validate.EditGroup;
 | 
			
		||||
import com.ruoyi.common.excel.utils.ExcelUtil;
 | 
			
		||||
import com.ruoyi.common.idempotent.annotation.RepeatSubmit;
 | 
			
		||||
import com.ruoyi.common.log.annotation.Log;
 | 
			
		||||
import com.ruoyi.common.log.enums.BusinessType;
 | 
			
		||||
import com.ruoyi.common.mybatis.core.page.PageQuery;
 | 
			
		||||
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
 | 
			
		||||
import com.ruoyi.common.tenant.helper.TenantHelper;
 | 
			
		||||
import com.ruoyi.common.web.core.BaseController;
 | 
			
		||||
import com.ruoyi.system.domain.bo.SysTenantBo;
 | 
			
		||||
import com.ruoyi.system.domain.bo.SysUserBo;
 | 
			
		||||
import com.ruoyi.system.domain.vo.SysTenantVo;
 | 
			
		||||
import com.ruoyi.system.service.ISysTenantService;
 | 
			
		||||
import com.ruoyi.system.service.ISysUserService;
 | 
			
		||||
import jakarta.servlet.http.HttpServletResponse;
 | 
			
		||||
import jakarta.validation.constraints.NotBlank;
 | 
			
		||||
import jakarta.validation.constraints.NotEmpty;
 | 
			
		||||
import jakarta.validation.constraints.NotNull;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import org.springframework.validation.annotation.Validated;
 | 
			
		||||
import org.springframework.web.bind.annotation.*;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 租户管理
 | 
			
		||||
 *
 | 
			
		||||
 * @author Michelle.Chung
 | 
			
		||||
 */
 | 
			
		||||
@Validated
 | 
			
		||||
@RequiredArgsConstructor
 | 
			
		||||
@RestController
 | 
			
		||||
@RequestMapping("/system/tenant")
 | 
			
		||||
public class SysTenantController extends BaseController {
 | 
			
		||||
 | 
			
		||||
    private final ISysTenantService sysTenantService;
 | 
			
		||||
    private final ISysUserService sysUserService;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 查询租户列表
 | 
			
		||||
     */
 | 
			
		||||
    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
 | 
			
		||||
    @SaCheckPermission("system:tenant:list")
 | 
			
		||||
    @GetMapping("/list")
 | 
			
		||||
    public TableDataInfo<SysTenantVo> list(SysTenantBo bo, PageQuery pageQuery) {
 | 
			
		||||
        return sysTenantService.queryPageList(bo, pageQuery);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 导出租户列表
 | 
			
		||||
     */
 | 
			
		||||
    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
 | 
			
		||||
    @SaCheckPermission("system:tenant:export")
 | 
			
		||||
    @Log(title = "租户", businessType = BusinessType.EXPORT)
 | 
			
		||||
    @PostMapping("/export")
 | 
			
		||||
    public void export(SysTenantBo bo, HttpServletResponse response) {
 | 
			
		||||
        List<SysTenantVo> list = sysTenantService.queryList(bo);
 | 
			
		||||
        ExcelUtil.exportExcel(list, "租户", SysTenantVo.class, response);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取租户详细信息
 | 
			
		||||
     *
 | 
			
		||||
     * @param id 主键
 | 
			
		||||
     */
 | 
			
		||||
    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
 | 
			
		||||
    @SaCheckPermission("system:tenant:query")
 | 
			
		||||
    @GetMapping("/{id}")
 | 
			
		||||
    public R<SysTenantVo> getInfo(@NotNull(message = "主键不能为空")
 | 
			
		||||
                                     @PathVariable Long id) {
 | 
			
		||||
        return R.ok(sysTenantService.queryById(id));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 新增租户
 | 
			
		||||
     */
 | 
			
		||||
    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
 | 
			
		||||
    @SaCheckPermission("system:tenant:add")
 | 
			
		||||
    @Log(title = "租户", businessType = BusinessType.INSERT)
 | 
			
		||||
    @Lock4j
 | 
			
		||||
    @RepeatSubmit()
 | 
			
		||||
    @PostMapping()
 | 
			
		||||
    public R<Void> add(@Validated(AddGroup.class) @RequestBody SysTenantBo bo) {
 | 
			
		||||
        if (TenantConstants.NOT_PASS.equals(sysTenantService.checkCompanyNameUnique(bo))) {
 | 
			
		||||
            throw new ServiceException("新增租户'" + bo.getCompanyName() + "'失败,企业名称已存在");
 | 
			
		||||
        }
 | 
			
		||||
        SysUserBo userBo = new SysUserBo();
 | 
			
		||||
        userBo.setUserName(bo.getUsername());
 | 
			
		||||
        // 判断用户名是否重复
 | 
			
		||||
        if (UserConstants.NOT_UNIQUE.equals(sysUserService.checkUserNameUnique(userBo))) {
 | 
			
		||||
            throw new ServiceException("新增用户'" + bo.getUsername() + "'失败,登录账号已存在");
 | 
			
		||||
        }
 | 
			
		||||
        return toAjax(sysTenantService.insertByBo(bo));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 修改租户
 | 
			
		||||
     */
 | 
			
		||||
    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
 | 
			
		||||
    @SaCheckPermission("system:tenant:edit")
 | 
			
		||||
    @Log(title = "租户", businessType = BusinessType.UPDATE)
 | 
			
		||||
    @RepeatSubmit()
 | 
			
		||||
    @PutMapping()
 | 
			
		||||
    public R<Void> edit(@Validated(EditGroup.class) @RequestBody SysTenantBo bo) {
 | 
			
		||||
        if (UserConstants.NOT_UNIQUE.equals(sysTenantService.checkCompanyNameUnique(bo))) {
 | 
			
		||||
            throw new ServiceException("修改租户'" + bo.getCompanyName() + "'失败,公司名称已存在");
 | 
			
		||||
        }
 | 
			
		||||
        return toAjax(sysTenantService.updateByBo(bo));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 状态修改
 | 
			
		||||
     */
 | 
			
		||||
    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
 | 
			
		||||
    @SaCheckPermission("system:tenantPackage:edit")
 | 
			
		||||
    @Log(title = "租户套餐", businessType = BusinessType.UPDATE)
 | 
			
		||||
    @PutMapping("/changeStatus")
 | 
			
		||||
    public R<Void> changeStatus(@RequestBody SysTenantBo bo) {
 | 
			
		||||
        return toAjax(sysTenantService.updateTenantStatus(bo));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 删除租户
 | 
			
		||||
     *
 | 
			
		||||
     * @param ids 主键串
 | 
			
		||||
     */
 | 
			
		||||
    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
 | 
			
		||||
    @SaCheckPermission("system:tenant:remove")
 | 
			
		||||
    @Log(title = "租户", businessType = BusinessType.DELETE)
 | 
			
		||||
    @DeleteMapping("/{ids}")
 | 
			
		||||
    public R<Void> remove(@NotEmpty(message = "主键不能为空")
 | 
			
		||||
                          @PathVariable Long[] ids) {
 | 
			
		||||
        return toAjax(sysTenantService.deleteWithValidByIds(List.of(ids), true));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 动态切换租户
 | 
			
		||||
     *
 | 
			
		||||
     * @param tenantId 租户ID
 | 
			
		||||
     */
 | 
			
		||||
    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
 | 
			
		||||
    @GetMapping("/dynamic/{tenantId}")
 | 
			
		||||
    public R<Void> dynamicTenant(@NotBlank(message = "租户ID不能为空") @PathVariable String tenantId) {
 | 
			
		||||
        TenantHelper.setDynamic(tenantId);
 | 
			
		||||
        return R.ok();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 清除动态租户
 | 
			
		||||
     */
 | 
			
		||||
    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
 | 
			
		||||
    @GetMapping("/dynamic/clear")
 | 
			
		||||
    public R<Void> dynamicClear() {
 | 
			
		||||
        TenantHelper.clearDynamic();
 | 
			
		||||
        return R.ok();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,124 @@
 | 
			
		||||
package com.ruoyi.system.controller.system;
 | 
			
		||||
 | 
			
		||||
import cn.dev33.satoken.annotation.SaCheckPermission;
 | 
			
		||||
import cn.dev33.satoken.annotation.SaCheckRole;
 | 
			
		||||
import com.ruoyi.common.core.constant.TenantConstants;
 | 
			
		||||
import com.ruoyi.common.core.domain.R;
 | 
			
		||||
import com.ruoyi.common.core.validate.AddGroup;
 | 
			
		||||
import com.ruoyi.common.core.validate.EditGroup;
 | 
			
		||||
import com.ruoyi.common.excel.utils.ExcelUtil;
 | 
			
		||||
import com.ruoyi.common.idempotent.annotation.RepeatSubmit;
 | 
			
		||||
import com.ruoyi.common.log.annotation.Log;
 | 
			
		||||
import com.ruoyi.common.log.enums.BusinessType;
 | 
			
		||||
import com.ruoyi.common.mybatis.core.page.PageQuery;
 | 
			
		||||
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
 | 
			
		||||
import com.ruoyi.common.web.core.BaseController;
 | 
			
		||||
import com.ruoyi.system.domain.bo.SysTenantPackageBo;
 | 
			
		||||
import com.ruoyi.system.domain.vo.SysTenantPackageVo;
 | 
			
		||||
import com.ruoyi.system.service.ISysTenantPackageService;
 | 
			
		||||
import jakarta.servlet.http.HttpServletResponse;
 | 
			
		||||
import jakarta.validation.constraints.NotEmpty;
 | 
			
		||||
import jakarta.validation.constraints.NotNull;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import org.springframework.validation.annotation.Validated;
 | 
			
		||||
import org.springframework.web.bind.annotation.*;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 租户套餐管理
 | 
			
		||||
 *
 | 
			
		||||
 * @author Michelle.Chung
 | 
			
		||||
 */
 | 
			
		||||
@Validated
 | 
			
		||||
@RequiredArgsConstructor
 | 
			
		||||
@RestController
 | 
			
		||||
@RequestMapping("/system/tenant/package")
 | 
			
		||||
public class SysTenantPackageController extends BaseController {
 | 
			
		||||
 | 
			
		||||
    private final ISysTenantPackageService sysTenantPackageService;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 查询租户套餐列表
 | 
			
		||||
     */
 | 
			
		||||
    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
 | 
			
		||||
    @SaCheckPermission("system:tenantPackage:list")
 | 
			
		||||
    @GetMapping("/list")
 | 
			
		||||
    public TableDataInfo<SysTenantPackageVo> list(SysTenantPackageBo bo, PageQuery pageQuery) {
 | 
			
		||||
        return sysTenantPackageService.queryPageList(bo, pageQuery);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 导出租户套餐列表
 | 
			
		||||
     */
 | 
			
		||||
    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
 | 
			
		||||
    @SaCheckPermission("system:tenantPackage:export")
 | 
			
		||||
    @Log(title = "租户套餐", businessType = BusinessType.EXPORT)
 | 
			
		||||
    @PostMapping("/export")
 | 
			
		||||
    public void export(SysTenantPackageBo bo, HttpServletResponse response) {
 | 
			
		||||
        List<SysTenantPackageVo> list = sysTenantPackageService.queryList(bo);
 | 
			
		||||
        ExcelUtil.exportExcel(list, "租户套餐", SysTenantPackageVo.class, response);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取租户套餐详细信息
 | 
			
		||||
     *
 | 
			
		||||
     * @param packageId 主键
 | 
			
		||||
     */
 | 
			
		||||
    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
 | 
			
		||||
    @SaCheckPermission("system:tenantPackage:query")
 | 
			
		||||
    @GetMapping("/{packageId}")
 | 
			
		||||
    public R<SysTenantPackageVo> getInfo(@NotNull(message = "主键不能为空")
 | 
			
		||||
                                     @PathVariable Long packageId) {
 | 
			
		||||
        return R.ok(sysTenantPackageService.queryById(packageId));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 新增租户套餐
 | 
			
		||||
     */
 | 
			
		||||
    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
 | 
			
		||||
    @SaCheckPermission("system:tenantPackage:add")
 | 
			
		||||
    @Log(title = "租户套餐", businessType = BusinessType.INSERT)
 | 
			
		||||
    @RepeatSubmit()
 | 
			
		||||
    @PostMapping()
 | 
			
		||||
    public R<Void> add(@Validated(AddGroup.class) @RequestBody SysTenantPackageBo bo) {
 | 
			
		||||
        return toAjax(sysTenantPackageService.insertByBo(bo));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 修改租户套餐
 | 
			
		||||
     */
 | 
			
		||||
    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
 | 
			
		||||
    @SaCheckPermission("system:tenantPackage:edit")
 | 
			
		||||
    @Log(title = "租户套餐", businessType = BusinessType.UPDATE)
 | 
			
		||||
    @RepeatSubmit()
 | 
			
		||||
    @PutMapping()
 | 
			
		||||
    public R<Void> edit(@Validated(EditGroup.class) @RequestBody SysTenantPackageBo bo) {
 | 
			
		||||
        return toAjax(sysTenantPackageService.updateByBo(bo));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 状态修改
 | 
			
		||||
     */
 | 
			
		||||
    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
 | 
			
		||||
    @SaCheckPermission("system:tenantPackage:edit")
 | 
			
		||||
    @Log(title = "租户套餐", businessType = BusinessType.UPDATE)
 | 
			
		||||
    @PutMapping("/changeStatus")
 | 
			
		||||
    public R<Void> changeStatus(@RequestBody SysTenantPackageBo bo) {
 | 
			
		||||
        return toAjax(sysTenantPackageService.updatePackageStatus(bo));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 删除租户套餐
 | 
			
		||||
     *
 | 
			
		||||
     * @param packageIds 主键串
 | 
			
		||||
     */
 | 
			
		||||
    @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY)
 | 
			
		||||
    @SaCheckPermission("system:tenantPackage:remove")
 | 
			
		||||
    @Log(title = "租户套餐", businessType = BusinessType.DELETE)
 | 
			
		||||
    @DeleteMapping("/{packageIds}")
 | 
			
		||||
    public R<Void> remove(@NotEmpty(message = "主键不能为空")
 | 
			
		||||
                          @PathVariable Long[] packageIds) {
 | 
			
		||||
        return toAjax(sysTenantPackageService.deleteWithValidByIds(List.of(packageIds), true));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -6,6 +6,7 @@ import cn.hutool.core.bean.BeanUtil;
 | 
			
		||||
import cn.hutool.core.lang.tree.Tree;
 | 
			
		||||
import cn.hutool.core.util.ArrayUtil;
 | 
			
		||||
import cn.hutool.core.util.ObjectUtil;
 | 
			
		||||
import com.ruoyi.common.core.constant.TenantConstants;
 | 
			
		||||
import com.ruoyi.common.core.constant.UserConstants;
 | 
			
		||||
import com.ruoyi.common.core.domain.R;
 | 
			
		||||
import com.ruoyi.common.core.utils.StreamUtils;
 | 
			
		||||
@@ -16,16 +17,14 @@ import com.ruoyi.common.log.annotation.Log;
 | 
			
		||||
import com.ruoyi.common.log.enums.BusinessType;
 | 
			
		||||
import com.ruoyi.common.mybatis.core.page.PageQuery;
 | 
			
		||||
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
 | 
			
		||||
import com.ruoyi.common.tenant.helper.TenantHelper;
 | 
			
		||||
import com.ruoyi.common.satoken.utils.LoginHelper;
 | 
			
		||||
import com.ruoyi.common.web.core.BaseController;
 | 
			
		||||
import com.ruoyi.system.domain.SysDept;
 | 
			
		||||
import com.ruoyi.system.domain.bo.SysUserBo;
 | 
			
		||||
import com.ruoyi.system.domain.vo.*;
 | 
			
		||||
import com.ruoyi.system.listener.SysUserImportListener;
 | 
			
		||||
import com.ruoyi.system.service.ISysDeptService;
 | 
			
		||||
import com.ruoyi.system.service.ISysPostService;
 | 
			
		||||
import com.ruoyi.system.service.ISysRoleService;
 | 
			
		||||
import com.ruoyi.system.service.ISysUserService;
 | 
			
		||||
import com.ruoyi.system.service.*;
 | 
			
		||||
import jakarta.servlet.http.HttpServletResponse;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import org.springframework.http.MediaType;
 | 
			
		||||
@@ -51,6 +50,7 @@ public class SysUserController extends BaseController {
 | 
			
		||||
    private final ISysRoleService roleService;
 | 
			
		||||
    private final ISysPostService postService;
 | 
			
		||||
    private final ISysDeptService deptService;
 | 
			
		||||
    private final ISysTenantService tenantService;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取用户列表
 | 
			
		||||
@@ -114,7 +114,7 @@ public class SysUserController extends BaseController {
 | 
			
		||||
        userService.checkUserDataScope(userId);
 | 
			
		||||
        SysUserInfoVo userInfoVo = new SysUserInfoVo();
 | 
			
		||||
        List<SysRoleVo> roles = roleService.selectRoleAll();
 | 
			
		||||
        userInfoVo.setRoles(LoginHelper.isAdmin(userId) ? roles : StreamUtils.filter(roles, r -> !r.isAdmin()));
 | 
			
		||||
        userInfoVo.setRoles(LoginHelper.isSuperAdmin(userId) ? roles : StreamUtils.filter(roles, r -> !r.isSuperAdmin()));
 | 
			
		||||
        userInfoVo.setPosts(postService.selectPostAll());
 | 
			
		||||
        if (ObjectUtil.isNotNull(userId)) {
 | 
			
		||||
            SysUserVo sysUser = userService.selectUserById(userId);
 | 
			
		||||
@@ -141,6 +141,12 @@ public class SysUserController extends BaseController {
 | 
			
		||||
                   && UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user))) {
 | 
			
		||||
            return R.fail("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在");
 | 
			
		||||
        }
 | 
			
		||||
        if (TenantHelper.isEnable()) {
 | 
			
		||||
            String status = tenantService.checkAccountBalance(LoginHelper.getTenantId());
 | 
			
		||||
            if (TenantConstants.NOT_PASS.equals(status)) {
 | 
			
		||||
                return R.fail("当前租户下用户名额不足,请联系管理员");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        user.setPassword(BCrypt.hashpw(user.getPassword()));
 | 
			
		||||
        return toAjax(userService.insertUser(user));
 | 
			
		||||
    }
 | 
			
		||||
@@ -218,7 +224,7 @@ public class SysUserController extends BaseController {
 | 
			
		||||
        List<SysRoleVo> roles = roleService.selectRolesByUserId(userId);
 | 
			
		||||
        SysUserInfoVo userInfoVo = new SysUserInfoVo();
 | 
			
		||||
        userInfoVo.setUser(user);
 | 
			
		||||
        userInfoVo.setRoles(LoginHelper.isAdmin(userId) ? roles : StreamUtils.filter(roles, r -> !r.isAdmin()));
 | 
			
		||||
        userInfoVo.setRoles(LoginHelper.isSuperAdmin(userId) ? roles : StreamUtils.filter(roles, r -> !r.isSuperAdmin()));
 | 
			
		||||
        return R.ok(userInfoVo);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ package com.ruoyi.system.domain;
 | 
			
		||||
 | 
			
		||||
import com.baomidou.mybatisplus.annotation.TableId;
 | 
			
		||||
import com.baomidou.mybatisplus.annotation.TableName;
 | 
			
		||||
import com.ruoyi.common.mybatis.core.domain.BaseEntity;
 | 
			
		||||
import com.ruoyi.common.tenant.core.TenantEntity;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.EqualsAndHashCode;
 | 
			
		||||
 | 
			
		||||
@@ -15,7 +15,7 @@ import lombok.EqualsAndHashCode;
 | 
			
		||||
@Data
 | 
			
		||||
@EqualsAndHashCode(callSuper = true)
 | 
			
		||||
@TableName("sys_config")
 | 
			
		||||
public class SysConfig extends BaseEntity {
 | 
			
		||||
public class SysConfig extends TenantEntity {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 参数主键
 | 
			
		||||
 
 | 
			
		||||
@@ -3,11 +3,7 @@ package com.ruoyi.system.domain;
 | 
			
		||||
import com.baomidou.mybatisplus.annotation.TableId;
 | 
			
		||||
import com.baomidou.mybatisplus.annotation.TableLogic;
 | 
			
		||||
import com.baomidou.mybatisplus.annotation.TableName;
 | 
			
		||||
import com.ruoyi.common.mybatis.core.domain.TreeEntity;
 | 
			
		||||
import jakarta.validation.constraints.Email;
 | 
			
		||||
import jakarta.validation.constraints.NotBlank;
 | 
			
		||||
import jakarta.validation.constraints.NotNull;
 | 
			
		||||
import jakarta.validation.constraints.Size;
 | 
			
		||||
import com.ruoyi.common.tenant.core.TenantEntity;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.EqualsAndHashCode;
 | 
			
		||||
 | 
			
		||||
@@ -22,7 +18,7 @@ import java.io.Serial;
 | 
			
		||||
@Data
 | 
			
		||||
@EqualsAndHashCode(callSuper = true)
 | 
			
		||||
@TableName("sys_dept")
 | 
			
		||||
public class SysDept extends TreeEntity<SysDept> {
 | 
			
		||||
public class SysDept extends TenantEntity {
 | 
			
		||||
 | 
			
		||||
    @Serial
 | 
			
		||||
    private static final long serialVersionUID = 1L;
 | 
			
		||||
@@ -33,6 +29,11 @@ public class SysDept extends TreeEntity<SysDept> {
 | 
			
		||||
    @TableId(value = "dept_id")
 | 
			
		||||
    private Long deptId;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 父部门ID
 | 
			
		||||
     */
 | 
			
		||||
    private Long parentId;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 部门名称
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@ package com.ruoyi.system.domain;
 | 
			
		||||
import com.baomidou.mybatisplus.annotation.TableId;
 | 
			
		||||
import com.baomidou.mybatisplus.annotation.TableName;
 | 
			
		||||
import com.ruoyi.common.core.constant.UserConstants;
 | 
			
		||||
import com.ruoyi.common.mybatis.core.domain.BaseEntity;
 | 
			
		||||
import com.ruoyi.common.tenant.core.TenantEntity;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.EqualsAndHashCode;
 | 
			
		||||
 | 
			
		||||
@@ -16,7 +16,7 @@ import lombok.EqualsAndHashCode;
 | 
			
		||||
@Data
 | 
			
		||||
@EqualsAndHashCode(callSuper = true)
 | 
			
		||||
@TableName("sys_dict_data")
 | 
			
		||||
public class SysDictData extends BaseEntity {
 | 
			
		||||
public class SysDictData extends TenantEntity {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 字典编码
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ package com.ruoyi.system.domain;
 | 
			
		||||
 | 
			
		||||
import com.baomidou.mybatisplus.annotation.TableId;
 | 
			
		||||
import com.baomidou.mybatisplus.annotation.TableName;
 | 
			
		||||
import com.ruoyi.common.mybatis.core.domain.BaseEntity;
 | 
			
		||||
import com.ruoyi.common.tenant.core.TenantEntity;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.EqualsAndHashCode;
 | 
			
		||||
 | 
			
		||||
@@ -15,7 +15,7 @@ import lombok.EqualsAndHashCode;
 | 
			
		||||
@Data
 | 
			
		||||
@EqualsAndHashCode(callSuper = true)
 | 
			
		||||
@TableName("sys_dict_type")
 | 
			
		||||
public class SysDictType extends BaseEntity {
 | 
			
		||||
public class SysDictType extends TenantEntity {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 字典主键
 | 
			
		||||
 
 | 
			
		||||
@@ -1,19 +1,12 @@
 | 
			
		||||
package com.ruoyi.system.domain;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
 | 
			
		||||
import com.alibaba.excel.annotation.ExcelProperty;
 | 
			
		||||
import com.baomidou.mybatisplus.annotation.TableField;
 | 
			
		||||
import com.baomidou.mybatisplus.annotation.TableId;
 | 
			
		||||
import com.baomidou.mybatisplus.annotation.TableName;
 | 
			
		||||
import com.ruoyi.common.excel.annotation.ExcelDictFormat;
 | 
			
		||||
import com.ruoyi.common.excel.convert.ExcelDictConvert;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
 | 
			
		||||
import java.io.Serial;
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 系统访问记录表 sys_logininfor
 | 
			
		||||
@@ -23,7 +16,6 @@ import java.util.Map;
 | 
			
		||||
 | 
			
		||||
@Data
 | 
			
		||||
@TableName("sys_logininfor")
 | 
			
		||||
@ExcelIgnoreUnannotated
 | 
			
		||||
public class SysLogininfor implements Serializable {
 | 
			
		||||
 | 
			
		||||
    @Serial
 | 
			
		||||
@@ -32,63 +24,52 @@ public class SysLogininfor implements Serializable {
 | 
			
		||||
    /**
 | 
			
		||||
     * ID
 | 
			
		||||
     */
 | 
			
		||||
    @ExcelProperty(value = "序号")
 | 
			
		||||
    @TableId(value = "info_id")
 | 
			
		||||
    private Long infoId;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 租户编号
 | 
			
		||||
     */
 | 
			
		||||
    private String tenantId;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 用户账号
 | 
			
		||||
     */
 | 
			
		||||
    @ExcelProperty(value = "用户账号")
 | 
			
		||||
    private String userName;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 登录状态 0成功 1失败
 | 
			
		||||
     */
 | 
			
		||||
    @ExcelProperty(value = "登录状态", converter = ExcelDictConvert.class)
 | 
			
		||||
    @ExcelDictFormat(dictType = "sys_common_status")
 | 
			
		||||
    private String status;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 登录IP地址
 | 
			
		||||
     */
 | 
			
		||||
    @ExcelProperty(value = "登录地址")
 | 
			
		||||
    private String ipaddr;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 登录地点
 | 
			
		||||
     */
 | 
			
		||||
    @ExcelProperty(value = "登录地点")
 | 
			
		||||
    private String loginLocation;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 浏览器类型
 | 
			
		||||
     */
 | 
			
		||||
    @ExcelProperty(value = "浏览器")
 | 
			
		||||
    private String browser;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 操作系统
 | 
			
		||||
     */
 | 
			
		||||
    @ExcelProperty(value = "操作系统")
 | 
			
		||||
    private String os;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 提示消息
 | 
			
		||||
     */
 | 
			
		||||
    @ExcelProperty(value = "提示消息")
 | 
			
		||||
    private String msg;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 访问时间
 | 
			
		||||
     */
 | 
			
		||||
    @ExcelProperty(value = "访问时间")
 | 
			
		||||
    private Date loginTime;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 请求参数
 | 
			
		||||
     */
 | 
			
		||||
    @TableField(exist = false)
 | 
			
		||||
    private Map<String, Object> params = new HashMap<>();
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,18 @@
 | 
			
		||||
package com.ruoyi.system.domain;
 | 
			
		||||
 | 
			
		||||
import com.baomidou.mybatisplus.annotation.TableField;
 | 
			
		||||
import com.baomidou.mybatisplus.annotation.TableId;
 | 
			
		||||
import com.baomidou.mybatisplus.annotation.TableName;
 | 
			
		||||
import com.ruoyi.common.core.constant.Constants;
 | 
			
		||||
import com.ruoyi.common.core.constant.UserConstants;
 | 
			
		||||
import com.ruoyi.common.core.utils.StringUtils;
 | 
			
		||||
import com.ruoyi.common.mybatis.core.domain.TreeEntity;
 | 
			
		||||
import com.ruoyi.common.mybatis.core.domain.BaseEntity;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.EqualsAndHashCode;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 菜单权限表 sys_menu
 | 
			
		||||
 *
 | 
			
		||||
@@ -18,7 +22,7 @@ import lombok.EqualsAndHashCode;
 | 
			
		||||
@Data
 | 
			
		||||
@EqualsAndHashCode(callSuper = true)
 | 
			
		||||
@TableName("sys_menu")
 | 
			
		||||
public class SysMenu extends TreeEntity<SysMenu> {
 | 
			
		||||
public class SysMenu extends BaseEntity {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 菜单ID
 | 
			
		||||
@@ -26,6 +30,11 @@ public class SysMenu extends TreeEntity<SysMenu> {
 | 
			
		||||
    @TableId(value = "menu_id")
 | 
			
		||||
    private Long menuId;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 父菜单ID
 | 
			
		||||
     */
 | 
			
		||||
    private Long parentId;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 菜单名称
 | 
			
		||||
     */
 | 
			
		||||
@@ -91,6 +100,18 @@ public class SysMenu extends TreeEntity<SysMenu> {
 | 
			
		||||
     */
 | 
			
		||||
    private String remark;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 父菜单名称
 | 
			
		||||
     */
 | 
			
		||||
    @TableField(exist = false)
 | 
			
		||||
    private String parentName;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 子菜单
 | 
			
		||||
     */
 | 
			
		||||
    @TableField(exist = false)
 | 
			
		||||
    private List<SysMenu> children = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取路由名称
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ package com.ruoyi.system.domain;
 | 
			
		||||
 | 
			
		||||
import com.baomidou.mybatisplus.annotation.TableId;
 | 
			
		||||
import com.baomidou.mybatisplus.annotation.TableName;
 | 
			
		||||
import com.ruoyi.common.mybatis.core.domain.BaseEntity;
 | 
			
		||||
import com.ruoyi.common.tenant.core.TenantEntity;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.EqualsAndHashCode;
 | 
			
		||||
 | 
			
		||||
@@ -15,7 +15,7 @@ import lombok.EqualsAndHashCode;
 | 
			
		||||
@Data
 | 
			
		||||
@EqualsAndHashCode(callSuper = true)
 | 
			
		||||
@TableName("sys_notice")
 | 
			
		||||
public class SysNotice extends BaseEntity {
 | 
			
		||||
public class SysNotice extends TenantEntity {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 公告ID
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user