!562 ♥️发布 5.2.1 正式版本
Merge pull request !562 from 疯狂的狮子Li/dev
| | |
| | | <configuration default="false" name="ruoyi-monitor-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker"> |
| | | <deployment type="dockerfile"> |
| | | <settings> |
| | | <option name="imageTag" value="ruoyi/ruoyi-monitor-admin:5.2.0" /> |
| | | <option name="imageTag" value="ruoyi/ruoyi-monitor-admin:5.2.1" /> |
| | | <option name="buildOnly" value="true" /> |
| | | <option name="sourceFilePath" value="ruoyi-extend/ruoyi-monitor-admin/Dockerfile" /> |
| | | </settings> |
| | |
| | | <configuration default="false" name="ruoyi-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker"> |
| | | <deployment type="dockerfile"> |
| | | <settings> |
| | | <option name="imageTag" value="ruoyi/ruoyi-server:5.2.0" /> |
| | | <option name="imageTag" value="ruoyi/ruoyi-server:5.2.1" /> |
| | | <option name="buildOnly" value="true" /> |
| | | <option name="sourceFilePath" value="ruoyi-admin/Dockerfile" /> |
| | | </settings> |
| | |
| | | <configuration default="false" name="ruoyi-snailjob-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker"> |
| | | <deployment type="dockerfile"> |
| | | <settings> |
| | | <option name="imageTag" value="ruoyi/ruoyi-snailjob-server:5.2.0" /> |
| | | <option name="imageTag" value="ruoyi/ruoyi-snailjob-server:5.2.1" /> |
| | | <option name="buildOnly" value="true" /> |
| | | <option name="sourceFilePath" value="ruoyi-extend/ruoyi-snailjob-server/Dockerfile" /> |
| | | </settings> |
| | |
| | | [](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/master/LICENSE) |
| | | [](https://www.jetbrains.com/?from=RuoYi-Vue-Plus) |
| | | <br> |
| | | [](https://gitee.com/dromara/RuoYi-Vue-Plus) |
| | | [](https://gitee.com/dromara/RuoYi-Vue-Plus) |
| | | []() |
| | | []() |
| | | []() |
| | |
| | | | 数据加解密 | 采用 注解 + mybatis 拦截器 对存取数据期间自动加解密<br/>支持多种策略 如BASE64、AES、RSA、SM2、SM4等 | 无 | |
| | | | 接口传输加密 | 采用 动态 AES + RSA 加密请求 body 每一次请求秘钥都不同大幅度降低可破解性 | 无 | |
| | | | 数据翻译 | 采用 注解 + jackson 序列化期间动态修改数据 数据进行翻译<br/>支持多种模式: `映射翻译` `直接翻译` `其他扩展条件翻译` 接口化两步即可完成自定义扩展 内置多种翻译实现 | 无 | |
| | | | 多数据源框架 | 采用 dynamic-datasource 支持世面大部分数据库<br/>通过yml配置即可动态管理异构不同种类的数据库 也可通过前端页面添加数据源<br/>支持spel表达式从请求头参数等条件切换数据源 | 基于 druid 手动编写代码配置数据源 配置繁琐 支持性差 | |
| | | | 多数据源框架 | 采用 dynamic-datasource 支持市面大部分数据库<br/>通过yml配置即可动态管理异构不同种类的数据库 也可通过前端页面添加数据源<br/>支持spel表达式从请求头参数等条件切换数据源 | 基于 druid 手动编写代码配置数据源 配置繁琐 支持性差 | |
| | | | 多数据源事务 | 采用 dynamic-datasource 支持多数据源不同种类的数据库事务回滚 | 不支持 | |
| | | | 数据库连接池 | 采用 HikariCP Spring官方内置连接池 配置简单 以性能与稳定性闻名天下 | 采用 druid bug众多 社区维护差 活跃度低 配置众多繁琐性能一般 | |
| | | | 数据库主键 | 采用 雪花ID 基于时间戳的 有序增长 唯一ID 再也不用为分库分表 数据合并主键冲突重复而发愁 | 采用 数据库自增ID 支持数据量有限 不支持多数据源主键唯一 | |
| | |
| | | <description>RuoYi-Vue-Plus多租户管理系统</description> |
| | | |
| | | <properties> |
| | | <revision>5.2.0</revision> |
| | | <revision>5.2.1</revision> |
| | | <spring-boot.version>3.2.6</spring-boot.version> |
| | | <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
| | | <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> |
| | |
| | | # 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/ |
| | | FROM bellsoft/liberica-openjdk-alpine:17.0.11-cds |
| | | #FROM bellsoft/liberica-openjdk-alpine:21.0.3-cds |
| | | #FROM findepi/graalvm:java17-native |
| | | FROM openjdk:17.0.2-oraclelinux8 |
| | | |
| | | MAINTAINER Lion Li |
| | | LABEL maintainer="Lion Li" |
| | | |
| | | RUN mkdir -p /ruoyi/server/logs \ |
| | | /ruoyi/server/temp \ |
| | |
| | | |
| | | WORKDIR /ruoyi/server |
| | | |
| | | # 补全alpine镜像缺失字体问题 |
| | | RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories |
| | | RUN apk add fontconfig && apk add --update ttf-dejavu && fc-cache --force |
| | | |
| | | ENV SERVER_PORT=8080 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS="" |
| | | |
| | | EXPOSE ${SERVER_PORT} |
| | |
| | | package org.dromara.web.controller; |
| | | |
| | | import cn.dev33.satoken.annotation.SaIgnore; |
| | | import cn.dev33.satoken.exception.NotLoginException; |
| | | import cn.hutool.core.codec.Base64; |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.util.ObjectUtil; |
| | |
| | | */ |
| | | @GetMapping("/tenant/list") |
| | | public R<LoginTenantVo> tenantList(HttpServletRequest request) throws Exception { |
| | | // 返回对象 |
| | | LoginTenantVo result = new LoginTenantVo(); |
| | | boolean enable = TenantHelper.isEnable(); |
| | | result.setTenantEnabled(enable); |
| | | // 如果未开启租户这直接返回 |
| | | if (!enable) { |
| | | return R.ok(result); |
| | | } |
| | | |
| | | List<SysTenantVo> tenantList = tenantService.queryList(new SysTenantBo()); |
| | | List<TenantListVo> voList = MapstructUtils.convert(tenantList, TenantListVo.class); |
| | | try { |
| | | // 如果只超管返回所有租户 |
| | | if (LoginHelper.isSuperAdmin()) { |
| | | result.setVoList(voList); |
| | | return R.ok(result); |
| | | } |
| | | } catch (NotLoginException ignored) { |
| | | } |
| | | |
| | | // 获取域名 |
| | | String host; |
| | | String referer = request.getHeader("referer"); |
| | |
| | | // 根据域名进行筛选 |
| | | List<TenantListVo> list = StreamUtils.filter(voList, vo -> |
| | | StringUtils.equals(vo.getDomain(), host)); |
| | | // 返回对象 |
| | | LoginTenantVo vo = new LoginTenantVo(); |
| | | vo.setVoList(CollUtil.isNotEmpty(list) ? list : voList); |
| | | vo.setTenantEnabled(TenantHelper.isEnable()); |
| | | return R.ok(vo); |
| | | result.setVoList(CollUtil.isNotEmpty(list) ? list : voList); |
| | | return R.ok(result); |
| | | } |
| | | |
| | | } |
| | |
| | | import org.dromara.common.satoken.utils.LoginHelper; |
| | | import org.dromara.common.tenant.helper.TenantHelper; |
| | | import org.dromara.common.web.config.properties.CaptchaProperties; |
| | | import org.dromara.system.domain.SysClient; |
| | | import org.dromara.system.domain.SysUser; |
| | | import org.dromara.system.domain.vo.SysClientVo; |
| | | import org.dromara.system.domain.vo.SysUserVo; |
| | |
| | | * @param uuid 唯一标识 |
| | | */ |
| | | private void validateCaptcha(String tenantId, String username, String code, String uuid) { |
| | | String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.defaultString(uuid, ""); |
| | | String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.blankToDefault(uuid, ""); |
| | | String captcha = RedisUtils.getCacheObject(verifyKey); |
| | | RedisUtils.deleteObject(verifyKey); |
| | | if (captcha == null) { |
| | |
| | | import org.dromara.common.core.enums.UserStatus; |
| | | import org.dromara.common.core.exception.ServiceException; |
| | | import org.dromara.common.core.exception.user.UserException; |
| | | import org.dromara.common.core.utils.StreamUtils; |
| | | import org.dromara.common.core.utils.ValidatorUtils; |
| | | import org.dromara.common.json.utils.JsonUtils; |
| | | import org.dromara.common.satoken.utils.LoginHelper; |
| | |
| | | } |
| | | SysSocialVo social; |
| | | if (TenantHelper.isEnable()) { |
| | | Optional<SysSocialVo> opt = list.stream().filter(x -> x.getTenantId().equals(loginBody.getTenantId())).findAny(); |
| | | Optional<SysSocialVo> opt = StreamUtils.findAny(list, x -> x.getTenantId().equals(loginBody.getTenantId())); |
| | | if (opt.isEmpty()) { |
| | | throw new ServiceException("对不起,你没有权限登录当前租户!"); |
| | | } |
| | |
| | | token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT" |
| | | server: |
| | | host: 127.0.0.1 |
| | | port: 1788 |
| | | port: 17888 |
| | | # 详见 script/sql/snail_job.sql `sj_namespace` 表 |
| | | namespace: ${spring.profiles.active} |
| | | # 随主应用端口飘逸 |
| | |
| | | port: 6379 |
| | | # 数据库索引 |
| | | database: 0 |
| | | # 密码(如没有密码请注释掉) |
| | | # password: |
| | | # redis 密码必须配置 |
| | | password: ruoyi123 |
| | | # 连接超时时间 |
| | | timeout: 10s |
| | | # 是否开启ssl |
| | |
| | | |
| | | --- # snail-job 配置 |
| | | snail-job: |
| | | enabled: false |
| | | enabled: true |
| | | # 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务 |
| | | group: "ruoyi_group" |
| | | # SnailJob 接入验证令牌 详见 script/sql/snail_job.sql `sj_group_config` 表 |
| | | token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT" |
| | | server: |
| | | host: 127.0.0.1 |
| | | port: 1788 |
| | | port: 17888 |
| | | # 详见 script/sql/snail_job.sql `sj_namespace` 表 |
| | | namespace: ${spring.profiles.active} |
| | | # 随主应用端口飘逸 |
| | |
| | | port: 6379 |
| | | # 数据库索引 |
| | | database: 0 |
| | | # 密码(如没有密码请注释掉) |
| | | # password: |
| | | # redis 密码必须配置 |
| | | password: ruoyi123 |
| | | # 连接超时时间 |
| | | timeout: 10s |
| | | # 是否开启ssl |
| | |
| | | </description> |
| | | |
| | | <properties> |
| | | <revision>5.2.0</revision> |
| | | <revision>5.2.1</revision> |
| | | </properties> |
| | | |
| | | <dependencyManagement> |
| | |
| | | |
| | | import java.util.*; |
| | | import java.util.function.BiFunction; |
| | | import java.util.function.Consumer; |
| | | import java.util.function.Function; |
| | | import java.util.function.Predicate; |
| | | import java.util.stream.Collectors; |
| | |
| | | } |
| | | |
| | | /** |
| | | * 找到流中满足条件的第一个元素 |
| | | * |
| | | * @param collection 需要查询的集合 |
| | | * @param function 过滤方法 |
| | | * @return 找到符合条件的第一个元素,没有则返回null |
| | | */ |
| | | public static <E> E findFirst(Collection<E> collection, Predicate<E> function) { |
| | | if (CollUtil.isEmpty(collection)) { |
| | | return null; |
| | | } |
| | | return collection.stream().filter(function).findFirst().orElse(null); |
| | | } |
| | | |
| | | /** |
| | | * 找到流中任意一个满足条件的元素 |
| | | * |
| | | * @param collection 需要查询的集合 |
| | | * @param function 过滤方法 |
| | | * @return 找到符合条件的任意一个元素,没有则返回null |
| | | */ |
| | | public static <E> Optional<E> findAny(Collection<E> collection, Predicate<E> function) { |
| | | if (CollUtil.isEmpty(collection)) { |
| | | return Optional.empty(); |
| | | } |
| | | return collection.stream().filter(function).findAny(); |
| | | } |
| | | |
| | | /** |
| | | * 将collection拼接 |
| | | * |
| | | * @param collection 需要转化的集合 |
| | |
| | | import io.swagger.v3.oas.models.tags.Tag; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.commons.lang3.StringUtils; |
| | | import org.dromara.common.core.utils.StreamUtils; |
| | | import org.springdoc.core.customizers.OpenApiBuilderCustomizer; |
| | | import org.springdoc.core.customizers.ServerBaseUrlCustomizer; |
| | | import org.springdoc.core.properties.SpringDocConfigProperties; |
| | |
| | | .flatMap(x -> Stream.of(x.value())).collect(Collectors.toSet()); |
| | | methodTags.addAll(AnnotatedElementUtils.findAllMergedAnnotations(method, io.swagger.v3.oas.annotations.tags.Tag.class)); |
| | | if (!CollectionUtils.isEmpty(methodTags)) { |
| | | tagsStr.addAll(methodTags.stream().map(tag -> propertyResolverUtils.resolve(tag.name(), locale)).collect(Collectors.toSet())); |
| | | tagsStr.addAll(StreamUtils.toSet(methodTags, tag -> propertyResolverUtils.resolve(tag.name(), locale))); |
| | | List<io.swagger.v3.oas.annotations.tags.Tag> allTags = new ArrayList<>(methodTags); |
| | | addTags(allTags, tags, locale); |
| | | } |
| | |
| | | import java.lang.annotation.*; |
| | | |
| | | /** |
| | | * 数据权限 |
| | | * |
| | | * 数据权限注解,用于标记数据权限的占位符关键字和替换值 |
| | | * <p> |
| | | * 一个注解只能对应一个模板 |
| | | * </p> |
| | | * |
| | | * @author Lion Li |
| | | * @version 3.5.0 |
| | |
| | | public @interface DataColumn { |
| | | |
| | | /** |
| | | * 占位符关键字 |
| | | * 数据权限模板的占位符关键字,默认为 "deptName" |
| | | * |
| | | * @return 占位符关键字数组 |
| | | */ |
| | | String[] key() default "deptName"; |
| | | |
| | | /** |
| | | * 占位符替换值 |
| | | * 数据权限模板的占位符替换值,默认为 "dept_id" |
| | | * |
| | | * @return 占位符替换值数组 |
| | | */ |
| | | String[] value() default "dept_id"; |
| | | |
| | |
| | | import java.lang.annotation.*; |
| | | |
| | | /** |
| | | * 数据权限组 |
| | | * 数据权限组注解,用于标记数据权限配置数组 |
| | | * |
| | | * @author Lion Li |
| | | * @version 3.5.0 |
| | |
| | | @Documented |
| | | public @interface DataPermission { |
| | | |
| | | /** |
| | | * 数据权限配置数组,用于指定数据权限的占位符关键字和替换值 |
| | | * |
| | | * @return 数据权限配置数组 |
| | | */ |
| | | DataColumn[] value(); |
| | | |
| | | } |
| | |
| | | * |
| | | * @author Lion Li |
| | | */ |
| | | |
| | | @Data |
| | | public class BaseEntity implements Serializable { |
| | | |
| | |
| | | import org.apache.ibatis.logging.Log; |
| | | import org.apache.ibatis.logging.LogFactory; |
| | | import org.dromara.common.core.utils.MapstructUtils; |
| | | import org.dromara.common.core.utils.StreamUtils; |
| | | |
| | | import java.io.Serializable; |
| | | import java.util.Collection; |
| | |
| | | |
| | | Log log = LogFactory.getLog(BaseMapperPlus.class); |
| | | |
| | | /** |
| | | * 获取当前实例对象关联的泛型类型 V 的 Class 对象 |
| | | * |
| | | * @return 返回当前实例对象关联的泛型类型 V 的 Class 对象 |
| | | */ |
| | | default Class<V> currentVoClass() { |
| | | return (Class<V>) GenericTypeUtils.resolveTypeArguments(this.getClass(), BaseMapperPlus.class)[1]; |
| | | } |
| | | |
| | | /** |
| | | * 获取当前实例对象关联的泛型类型 T 的 Class 对象 |
| | | * |
| | | * @return 返回当前实例对象关联的泛型类型 T 的 Class 对象 |
| | | */ |
| | | default Class<T> currentModelClass() { |
| | | return (Class<T>) GenericTypeUtils.resolveTypeArguments(this.getClass(), BaseMapperPlus.class)[0]; |
| | | } |
| | | |
| | | /** |
| | | * 使用默认的查询条件查询并返回结果列表 |
| | | * |
| | | * @return 返回查询结果的列表 |
| | | */ |
| | | default List<T> selectList() { |
| | | return this.selectList(new QueryWrapper<>()); |
| | | } |
| | | |
| | | /** |
| | | * 批量插入 |
| | | * 批量插入实体对象集合 |
| | | * |
| | | * @param entityList 实体对象集合 |
| | | * @return 插入操作是否成功的布尔值 |
| | | */ |
| | | default boolean insertBatch(Collection<T> entityList) { |
| | | Db.saveBatch(entityList); |
| | |
| | | } |
| | | |
| | | /** |
| | | * 批量更新 |
| | | * 批量根据ID更新实体对象集合 |
| | | * |
| | | * @param entityList 实体对象集合 |
| | | * @return 更新操作是否成功的布尔值 |
| | | */ |
| | | default boolean updateBatchById(Collection<T> entityList) { |
| | | Db.updateBatchById(entityList); |
| | |
| | | } |
| | | |
| | | /** |
| | | * 批量插入或更新 |
| | | * 批量插入或更新实体对象集合 |
| | | * |
| | | * @param entityList 实体对象集合 |
| | | * @return 插入或更新操作是否成功的布尔值 |
| | | */ |
| | | default boolean insertOrUpdateBatch(Collection<T> entityList) { |
| | | Db.saveOrUpdateBatch(entityList); |
| | |
| | | } |
| | | |
| | | /** |
| | | * 批量插入(包含限制条数) |
| | | * 批量插入实体对象集合并指定批处理大小 |
| | | * |
| | | * @param entityList 实体对象集合 |
| | | * @param batchSize 批处理大小 |
| | | * @return 插入操作是否成功的布尔值 |
| | | */ |
| | | default boolean insertBatch(Collection<T> entityList, int batchSize) { |
| | | Db.saveBatch(entityList, batchSize); |
| | |
| | | } |
| | | |
| | | /** |
| | | * 批量更新(包含限制条数) |
| | | * 批量根据ID更新实体对象集合并指定批处理大小 |
| | | * |
| | | * @param entityList 实体对象集合 |
| | | * @param batchSize 批处理大小 |
| | | * @return 更新操作是否成功的布尔值 |
| | | */ |
| | | default boolean updateBatchById(Collection<T> entityList, int batchSize) { |
| | | Db.updateBatchById(entityList, batchSize); |
| | |
| | | } |
| | | |
| | | /** |
| | | * 批量插入或更新(包含限制条数) |
| | | * 批量插入或更新实体对象集合并指定批处理大小 |
| | | * |
| | | * @param entityList 实体对象集合 |
| | | * @param batchSize 批处理大小 |
| | | * @return 插入或更新操作是否成功的布尔值 |
| | | */ |
| | | default boolean insertOrUpdateBatch(Collection<T> entityList, int batchSize) { |
| | | Db.saveOrUpdateBatch(entityList, batchSize); |
| | |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * 根据ID查询单个VO对象 |
| | | * |
| | | * @param id 主键ID |
| | | * @return 查询到的单个VO对象 |
| | | */ |
| | | default V selectVoById(Serializable id) { |
| | | return selectVoById(id, this.currentVoClass()); |
| | | } |
| | | |
| | | /** |
| | | * 根据 ID 查询 |
| | | * 根据ID查询单个VO对象并将其转换为指定的VO类 |
| | | * |
| | | * @param id 主键ID |
| | | * @param voClass 要转换的VO类的Class对象 |
| | | * @param <C> VO类的类型 |
| | | * @return 查询到的单个VO对象,经过转换为指定的VO类后返回 |
| | | */ |
| | | default <C> C selectVoById(Serializable id, Class<C> voClass) { |
| | | T obj = this.selectById(id); |
| | |
| | | return MapstructUtils.convert(obj, voClass); |
| | | } |
| | | |
| | | /** |
| | | * 根据ID集合批量查询VO对象列表 |
| | | * |
| | | * @param idList 主键ID集合 |
| | | * @return 查询到的VO对象列表 |
| | | */ |
| | | default List<V> selectVoBatchIds(Collection<? extends Serializable> idList) { |
| | | return selectVoBatchIds(idList, this.currentVoClass()); |
| | | } |
| | | |
| | | /** |
| | | * 查询(根据ID 批量查询) |
| | | * 根据ID集合批量查询实体对象列表,并将其转换为指定的VO对象列表 |
| | | * |
| | | * @param idList 主键ID集合 |
| | | * @param voClass 要转换的VO类的Class对象 |
| | | * @param <C> VO类的类型 |
| | | * @return 查询到的VO对象列表,经过转换为指定的VO类后返回 |
| | | */ |
| | | default <C> List<C> selectVoBatchIds(Collection<? extends Serializable> idList, Class<C> voClass) { |
| | | List<T> list = this.selectBatchIds(idList); |
| | |
| | | return MapstructUtils.convert(list, voClass); |
| | | } |
| | | |
| | | /** |
| | | * 根据查询条件Map查询VO对象列表 |
| | | * |
| | | * @param map 查询条件Map |
| | | * @return 查询到的VO对象列表 |
| | | */ |
| | | default List<V> selectVoByMap(Map<String, Object> map) { |
| | | return selectVoByMap(map, this.currentVoClass()); |
| | | } |
| | | |
| | | /** |
| | | * 查询(根据 columnMap 条件) |
| | | * 根据查询条件Map查询实体对象列表,并将其转换为指定的VO对象列表 |
| | | * |
| | | * @param map 查询条件Map |
| | | * @param voClass 要转换的VO类的Class对象 |
| | | * @param <C> VO类的类型 |
| | | * @return 查询到的VO对象列表,经过转换为指定的VO类后返回 |
| | | */ |
| | | default <C> List<C> selectVoByMap(Map<String, Object> map, Class<C> voClass) { |
| | | List<T> list = this.selectByMap(map); |
| | |
| | | return MapstructUtils.convert(list, voClass); |
| | | } |
| | | |
| | | /** |
| | | * 根据条件查询单个VO对象 |
| | | * |
| | | * @param wrapper 查询条件Wrapper |
| | | * @return 查询到的单个VO对象 |
| | | */ |
| | | default V selectVoOne(Wrapper<T> wrapper) { |
| | | return selectVoOne(wrapper, this.currentVoClass()); |
| | | } |
| | | |
| | | /** |
| | | * 根据条件查询单个VO对象,并根据需要决定是否抛出异常 |
| | | * |
| | | * @param wrapper 查询条件Wrapper |
| | | * @param throwEx 是否抛出异常的标志 |
| | | * @return 查询到的单个VO对象 |
| | | */ |
| | | default V selectVoOne(Wrapper<T> wrapper, boolean throwEx) { |
| | | return selectVoOne(wrapper, this.currentVoClass(), throwEx); |
| | | } |
| | | |
| | | /** |
| | | * 根据 entity 条件,查询一条记录 |
| | | * 根据条件查询单个VO对象,并指定返回的VO对象的类型 |
| | | * |
| | | * @param wrapper 查询条件Wrapper |
| | | * @param voClass 返回的VO对象的Class对象 |
| | | * @param <C> 返回的VO对象的类型 |
| | | * @return 查询到的单个VO对象,经过类型转换为指定的VO类后返回 |
| | | */ |
| | | default <C> C selectVoOne(Wrapper<T> wrapper, Class<C> voClass) { |
| | | return selectVoOne(wrapper, voClass, true); |
| | | } |
| | | |
| | | /** |
| | | * 根据 entity 条件,查询一条记录 |
| | | * 根据条件查询单个实体对象,并将其转换为指定的VO对象 |
| | | * |
| | | * @param wrapper 查询条件Wrapper |
| | | * @param voClass 要转换的VO类的Class对象 |
| | | * @param throwEx 是否抛出异常的标志 |
| | | * @param <C> VO类的类型 |
| | | * @return 查询到的单个VO对象,经过转换为指定的VO类后返回 |
| | | */ |
| | | default <C> C selectVoOne(Wrapper<T> wrapper, Class<C> voClass, boolean throwEx) { |
| | | T obj = this.selectOne(wrapper, throwEx); |
| | |
| | | return MapstructUtils.convert(obj, voClass); |
| | | } |
| | | |
| | | /** |
| | | * 查询所有VO对象列表 |
| | | * |
| | | * @return 查询到的VO对象列表 |
| | | */ |
| | | default List<V> selectVoList() { |
| | | return selectVoList(new QueryWrapper<>(), this.currentVoClass()); |
| | | } |
| | | |
| | | /** |
| | | * 根据条件查询VO对象列表 |
| | | * |
| | | * @param wrapper 查询条件Wrapper |
| | | * @return 查询到的VO对象列表 |
| | | */ |
| | | default List<V> selectVoList(Wrapper<T> wrapper) { |
| | | return selectVoList(wrapper, this.currentVoClass()); |
| | | } |
| | | |
| | | /** |
| | | * 根据 entity 条件,查询全部记录 |
| | | * 根据条件查询实体对象列表,并将其转换为指定的VO对象列表 |
| | | * |
| | | * @param wrapper 查询条件Wrapper |
| | | * @param voClass 要转换的VO类的Class对象 |
| | | * @param <C> VO类的类型 |
| | | * @return 查询到的VO对象列表,经过转换为指定的VO类后返回 |
| | | */ |
| | | default <C> List<C> selectVoList(Wrapper<T> wrapper, Class<C> voClass) { |
| | | List<T> list = this.selectList(wrapper); |
| | |
| | | return MapstructUtils.convert(list, voClass); |
| | | } |
| | | |
| | | /** |
| | | * 根据条件分页查询VO对象列表 |
| | | * |
| | | * @param page 分页信息 |
| | | * @param wrapper 查询条件Wrapper |
| | | * @return 查询到的VO对象分页列表 |
| | | */ |
| | | default <P extends IPage<V>> P selectVoPage(IPage<T> page, Wrapper<T> wrapper) { |
| | | return selectVoPage(page, wrapper, this.currentVoClass()); |
| | | } |
| | | |
| | | /** |
| | | * 分页查询VO |
| | | * 根据条件分页查询实体对象列表,并将其转换为指定的VO对象分页列表 |
| | | * |
| | | * @param page 分页信息 |
| | | * @param wrapper 查询条件Wrapper |
| | | * @param voClass 要转换的VO类的Class对象 |
| | | * @param <C> VO类的类型 |
| | | * @param <P> VO对象分页列表的类型 |
| | | * @return 查询到的VO对象分页列表,经过转换为指定的VO类后返回 |
| | | */ |
| | | default <C, P extends IPage<C>> P selectVoPage(IPage<T> page, Wrapper<T> wrapper, Class<C> voClass) { |
| | | // 根据条件分页查询实体对象列表 |
| | | List<T> list = this.selectList(page, wrapper); |
| | | // 创建一个新的VO对象分页列表,并设置分页信息 |
| | | IPage<C> voPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal()); |
| | | if (CollUtil.isEmpty(list)) { |
| | | return (P) voPage; |
| | |
| | | return (P) voPage; |
| | | } |
| | | |
| | | /** |
| | | * 根据条件查询符合条件的对象,并将其转换为指定类型的对象列表 |
| | | * |
| | | * @param wrapper 查询条件Wrapper |
| | | * @param mapper 转换函数,用于将查询到的对象转换为指定类型的对象 |
| | | * @param <C> 要转换的对象的类型 |
| | | * @return 查询到的符合条件的对象列表,经过转换为指定类型的对象后返回 |
| | | */ |
| | | 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()); |
| | | return StreamUtils.toList(this.selectObjs(wrapper), mapper); |
| | | } |
| | | |
| | | } |
| | |
| | | import cn.hutool.core.util.ObjectUtil; |
| | | import com.baomidou.mybatisplus.core.metadata.OrderItem; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import lombok.Data; |
| | | import org.dromara.common.core.exception.ServiceException; |
| | | import org.dromara.common.core.utils.StringUtils; |
| | | import org.dromara.common.core.utils.sql.SqlUtil; |
| | | import lombok.Data; |
| | | |
| | | import java.io.Serial; |
| | | import java.io.Serializable; |
| | |
| | | * |
| | | * @author Lion Li |
| | | */ |
| | | |
| | | @Data |
| | | public class PageQuery implements Serializable { |
| | | |
| | |
| | | */ |
| | | public static final int DEFAULT_PAGE_SIZE = Integer.MAX_VALUE; |
| | | |
| | | /** |
| | | * 构建分页对象 |
| | | */ |
| | | public <T> Page<T> build() { |
| | | Integer pageNum = ObjectUtil.defaultIfNull(getPageNum(), DEFAULT_PAGE_NUM); |
| | | Integer pageSize = ObjectUtil.defaultIfNull(getPageSize(), DEFAULT_PAGE_SIZE); |
| | |
| | | * |
| | | * @author Lion Li |
| | | */ |
| | | |
| | | @Data |
| | | @NoArgsConstructor |
| | | public class TableDataInfo<T> implements Serializable { |
| | |
| | | this.total = total; |
| | | } |
| | | |
| | | /** |
| | | * 根据分页对象构建表格分页数据对象 |
| | | */ |
| | | public static <T> TableDataInfo<T> build(IPage<T> page) { |
| | | TableDataInfo<T> rspData = new TableDataInfo<>(); |
| | | rspData.setCode(HttpStatus.HTTP_OK); |
| | |
| | | return rspData; |
| | | } |
| | | |
| | | /** |
| | | * 根据数据列表构建表格分页数据对象 |
| | | */ |
| | | public static <T> TableDataInfo<T> build(List<T> list) { |
| | | TableDataInfo<T> rspData = new TableDataInfo<>(); |
| | | rspData.setCode(HttpStatus.HTTP_OK); |
| | |
| | | return rspData; |
| | | } |
| | | |
| | | /** |
| | | * 构建表格分页数据对象 |
| | | */ |
| | | public static <T> TableDataInfo<T> build() { |
| | | TableDataInfo<T> rspData = new TableDataInfo<>(); |
| | | rspData.setCode(HttpStatus.HTTP_OK); |
| | |
| | | package org.dromara.common.mybatis.enums; |
| | | |
| | | import org.dromara.common.core.utils.StringUtils; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Getter; |
| | | import org.dromara.common.core.utils.StringUtils; |
| | | |
| | | /** |
| | | * 数据库类型 |
| | |
| | | */ |
| | | SQL_SERVER("Microsoft SQL Server"); |
| | | |
| | | /** |
| | | * 数据库类型 |
| | | */ |
| | | private final String type; |
| | | |
| | | /** |
| | | * 根据数据库产品名称查找对应的数据库类型 |
| | | * |
| | | * @param databaseProductName 数据库产品名称 |
| | | * @return 对应的数据库类型枚举值,如果未找到则返回 null |
| | | */ |
| | | public static DataBaseType find(String databaseProductName) { |
| | | if (StringUtils.isBlank(databaseProductName)) { |
| | | return null; |
| | |
| | | package org.dromara.common.mybatis.enums; |
| | | |
| | | import org.dromara.common.core.utils.StringUtils; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Getter; |
| | | import org.dromara.common.core.domain.model.LoginUser; |
| | | import org.dromara.common.core.utils.StringUtils; |
| | | import org.dromara.common.mybatis.helper.DataPermissionHelper; |
| | | |
| | | /** |
| | | * 数据权限类型 |
| | | * 数据权限类型枚举 |
| | | * <p> |
| | | * 语法支持 spel 模板表达式 |
| | | * <p> |
| | | * 内置数据 user 当前用户 内容参考 LoginUser |
| | | * 如需扩展数据 可使用 {@link DataPermissionHelper} 操作 |
| | | * 内置服务 sdss 系统数据权限服务 内容参考 SysDataScopeService |
| | | * 如需扩展更多自定义服务 可以参考 sdss 自行编写 |
| | | * 支持使用 SpEL 模板表达式定义 SQL 查询条件 |
| | | * 内置数据: |
| | | * - {@code user}: 当前登录用户信息,参考 {@link LoginUser} |
| | | * 内置服务: |
| | | * - {@code sdss}: 系统数据权限服务,参考 {@link ISysDataScopeService} |
| | | * 如需扩展数据,可以通过 {@link DataPermissionHelper} 进行操作 |
| | | * 如需扩展服务,可以通过 {@link ISysDataScopeService} 自行编写 |
| | | * </p> |
| | | * |
| | | * @author Lion Li |
| | | * @version 3.5.0 |
| | |
| | | |
| | | /** |
| | | * 自定数据权限 |
| | | * 使用 SpEL 表达式:`#{#deptName} IN ( #{@sdss.getRoleCustom( #user.roleId )} )` |
| | | * 如果不满足条件,则使用默认 SQL 表达式:`1 = 0` |
| | | */ |
| | | CUSTOM("2", " #{#deptName} IN ( #{@sdss.getRoleCustom( #user.roleId )} ) ", " 1 = 0 "), |
| | | |
| | | /** |
| | | * 部门数据权限 |
| | | * 使用 SpEL 表达式:`#{#deptName} = #{#user.deptId}` |
| | | * 如果不满足条件,则使用默认 SQL 表达式:`1 = 0` |
| | | */ |
| | | DEPT("3", " #{#deptName} = #{#user.deptId} ", " 1 = 0 "), |
| | | |
| | | /** |
| | | * 部门及以下数据权限 |
| | | * 使用 SpEL 表达式:`#{#deptName} IN ( #{@sdss.getDeptAndChild( #user.deptId )}` |
| | | * 如果不满足条件,则使用默认 SQL 表达式:`1 = 0` |
| | | */ |
| | | DEPT_AND_CHILD("4", " #{#deptName} IN ( #{@sdss.getDeptAndChild( #user.deptId )} )", " 1 = 0 "), |
| | | |
| | | /** |
| | | * 仅本人数据权限 |
| | | * 使用 SpEL 表达式:`#{#userName} = #{#user.userId}` |
| | | * 如果不满足条件,则使用默认 SQL 表达式:`1 = 0` |
| | | */ |
| | | SELF("5", " #{#userName} = #{#user.userId} ", " 1 = 0 "); |
| | | |
| | | private final String code; |
| | | |
| | | /** |
| | | * 语法 采用 spel 模板表达式 |
| | | * SpEL 模板表达式,用于构建 SQL 查询条件 |
| | | */ |
| | | private final String sqlTemplate; |
| | | |
| | | /** |
| | | * 不满足 sqlTemplate 则填充 |
| | | * 如果不满足 {@code sqlTemplate} 的条件,则使用此默认 SQL 表达式 |
| | | */ |
| | | private final String elseSql; |
| | | |
| | | /** |
| | | * 根据枚举代码查找对应的枚举值 |
| | | * |
| | | * @param code 枚举代码 |
| | | * @return 对应的枚举值,如果未找到则返回 null |
| | | */ |
| | | public static DataScopeType findCode(String code) { |
| | | if (StringUtils.isBlank(code)) { |
| | | return null; |
| | |
| | | import cn.hutool.core.util.ObjectUtil; |
| | | import cn.hutool.http.HttpStatus; |
| | | import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.ibatis.reflection.MetaObject; |
| | | import org.dromara.common.core.domain.model.LoginUser; |
| | | import org.dromara.common.core.exception.ServiceException; |
| | | import org.dromara.common.mybatis.core.domain.BaseEntity; |
| | | import org.dromara.common.satoken.utils.LoginHelper; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.ibatis.reflection.MetaObject; |
| | | |
| | | import java.util.Date; |
| | | |
| | |
| | | @Slf4j |
| | | public class InjectionMetaObjectHandler implements MetaObjectHandler { |
| | | |
| | | /** |
| | | * 插入填充方法,用于在插入数据时自动填充实体对象中的创建时间、更新时间、创建人、更新人等信息 |
| | | * |
| | | * @param metaObject 元对象,用于获取原始对象并进行填充 |
| | | */ |
| | | @Override |
| | | public void insertFill(MetaObject metaObject) { |
| | | try { |
| | | if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity baseEntity) { |
| | | // 获取当前时间作为创建时间和更新时间,如果创建时间不为空,则使用创建时间,否则使用当前时间 |
| | | Date current = ObjectUtil.isNotNull(baseEntity.getCreateTime()) |
| | | ? baseEntity.getCreateTime() : new Date(); |
| | | baseEntity.setCreateTime(current); |
| | | baseEntity.setUpdateTime(current); |
| | | |
| | | // 如果创建人为空,则填充当前登录用户的信息 |
| | | if (ObjectUtil.isNull(baseEntity.getCreateBy())) { |
| | | LoginUser loginUser = getLoginUser(); |
| | | if (ObjectUtil.isNotNull(loginUser)) { |
| | | Long userId = loginUser.getUserId(); |
| | | // 当前已登录 且 创建人为空 则填充 |
| | | // 填充创建人、更新人和创建部门信息 |
| | | baseEntity.setCreateBy(userId); |
| | | // 当前已登录 且 更新人为空 则填充 |
| | | baseEntity.setUpdateBy(userId); |
| | | baseEntity.setCreateDept(ObjectUtil.isNotNull(baseEntity.getCreateDept()) |
| | | ? baseEntity.getCreateDept() : loginUser.getDeptId()); |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 更新填充方法,用于在更新数据时自动填充实体对象中的更新时间和更新人信息 |
| | | * |
| | | * @param metaObject 元对象,用于获取原始对象并进行填充 |
| | | */ |
| | | @Override |
| | | public void updateFill(MetaObject metaObject) { |
| | | try { |
| | | if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity baseEntity) { |
| | | // 获取当前时间作为更新时间,无论原始对象中的更新时间是否为空都填充 |
| | | Date current = new Date(); |
| | | // 更新时间填充(不管为不为空) |
| | | baseEntity.setUpdateTime(current); |
| | | // 当前已登录 更新人填充(不管为不为空) |
| | | |
| | | // 获取当前登录用户的ID,并填充更新人信息 |
| | | Long userId = LoginHelper.getUserId(); |
| | | if (ObjectUtil.isNotNull(userId)) { |
| | | baseEntity.setUpdateBy(userId); |
| | | } |
| | | |
| | | } |
| | | } catch (Exception e) { |
| | | throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED); |
| | |
| | | } |
| | | |
| | | /** |
| | | * 获取登录用户名 |
| | | * 获取当前登录用户信息 |
| | | * |
| | | * @return 当前登录用户的信息,如果用户未登录则返回 null |
| | | */ |
| | | private LoginUser getLoginUser() { |
| | | LoginUser loginUser; |
| | |
| | | package org.dromara.common.mybatis.handler; |
| | | |
| | | import org.dromara.common.core.domain.R; |
| | | import jakarta.servlet.http.HttpServletRequest; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.dromara.common.core.domain.R; |
| | | import org.dromara.common.core.utils.StringUtils; |
| | | import org.mybatis.spring.MyBatisSystemException; |
| | | import org.springframework.dao.DuplicateKeyException; |
| | | import org.springframework.web.bind.annotation.ExceptionHandler; |
| | | import org.springframework.web.bind.annotation.RestControllerAdvice; |
| | | |
| | | import jakarta.servlet.http.HttpServletRequest; |
| | | |
| | | /** |
| | | * Mybatis异常处理器 |
| | |
| | | */ |
| | | private final BeanResolver beanResolver = new BeanFactoryResolver(SpringUtils.getBeanFactory()); |
| | | |
| | | /** |
| | | * 构造方法,扫描指定包下的 Mapper 类并初始化缓存 |
| | | * |
| | | * @param mapperPackage Mapper 类所在的包路径 |
| | | */ |
| | | public PlusDataPermissionHandler(String mapperPackage) { |
| | | scanMapperClasses(mapperPackage); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 获取数据过滤条件的 SQL 片段 |
| | | * |
| | | * @param where 原始的查询条件表达式 |
| | | * @param mappedStatementId Mapper 方法的 ID |
| | | * @param isSelect 是否为查询语句 |
| | | * @return 数据过滤条件的 SQL 片段 |
| | | */ |
| | | public Expression getSqlSegment(Expression where, String mappedStatementId, boolean isSelect) { |
| | | // 获取数据权限配置 |
| | | DataPermission dataPermission = getDataPermission(mappedStatementId); |
| | | // 获取当前登录用户信息 |
| | | LoginUser currentUser = DataPermissionHelper.getVariable("user"); |
| | | if (ObjectUtil.isNull(currentUser)) { |
| | | currentUser = LoginHelper.getLoginUser(); |
| | |
| | | if (LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin()) { |
| | | return where; |
| | | } |
| | | // 构造数据过滤条件的 SQL 片段 |
| | | String dataFilterSql = buildDataFilter(dataPermission.value(), isSelect); |
| | | if (StringUtils.isBlank(dataFilterSql)) { |
| | | return where; |
| | |
| | | } |
| | | |
| | | /** |
| | | * 构造数据过滤sql |
| | | * 构建数据过滤条件的 SQL 语句 |
| | | * |
| | | * @param dataColumns 数据权限注解中的列信息 |
| | | * @param isSelect 标志当前操作是否为查询操作,查询操作和更新或删除操作在处理过滤条件时会有不同的处理方式 |
| | | * @return 构建的数据过滤条件的 SQL 语句 |
| | | * @throws ServiceException 如果角色的数据范围异常或者 key 与 value 的长度不匹配,则抛出 ServiceException 异常 |
| | | */ |
| | | private String buildDataFilter(DataColumn[] dataColumns, boolean isSelect) { |
| | | // 更新或删除需满足所有条件 |
| | |
| | | } |
| | | |
| | | /** |
| | | * 通过 mapperPackage 设置的扫描包 扫描缓存有注解的方法与类 |
| | | * 扫描指定包下的 Mapper 类,并查找其中带有特定注解的方法或类 |
| | | * |
| | | * @param mapperPackage Mapper 类所在的包路径 |
| | | */ |
| | | private void scanMapperClasses(String mapperPackage) { |
| | | // 创建资源解析器和元数据读取工厂 |
| | | PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); |
| | | CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory(); |
| | | // 将 Mapper 包路径按分隔符拆分为数组 |
| | | String[] packagePatternArray = StringUtils.splitPreserveAllTokens(mapperPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); |
| | | String classpath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX; |
| | | try { |
| | | for (String packagePattern : packagePatternArray) { |
| | | // 将包路径转换为资源路径 |
| | | String path = ClassUtils.convertClassNameToResourcePath(packagePattern); |
| | | // 获取指定路径下的所有 .class 文件资源 |
| | | Resource[] resources = resolver.getResources(classpath + path + "/*.class"); |
| | | for (Resource resource : resources) { |
| | | // 获取资源的类元数据 |
| | | ClassMetadata classMetadata = factory.getMetadataReader(resource).getClassMetadata(); |
| | | // 获取资源对应的类对象 |
| | | Class<?> clazz = Resources.classForName(classMetadata.getClassName()); |
| | | // 查找类中的特定注解 |
| | | findAnnotation(clazz); |
| | | } |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 在指定的类中查找特定的注解 DataPermission,并将带有这个注解的方法或类存储到 dataPermissionCacheMap 中 |
| | | * |
| | | * @param clazz 要查找的类 |
| | | */ |
| | | private void findAnnotation(Class<?> clazz) { |
| | | DataPermission dataPermission; |
| | | // 获取方法注解 |
| | | for (Method method : clazz.getMethods()) { |
| | | if (method.isDefault() || method.isVarArgs()) { |
| | | continue; |
| | |
| | | dataPermissionCacheMap.put(mappedStatementId, dataPermission); |
| | | } |
| | | } |
| | | // 获取类注解 |
| | | if (AnnotationUtil.hasAnnotation(clazz, DataPermission.class)) { |
| | | dataPermission = AnnotationUtil.getAnnotation(clazz, DataPermission.class); |
| | | dataPermissionCacheMap.put(clazz.getName(), dataPermission); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 根据映射语句 ID 或类名获取对应的 DataPermission 注解对象 |
| | | * |
| | | * @param mapperId 映射语句 ID |
| | | * @return DataPermission 注解对象,如果不存在则返回 null |
| | | */ |
| | | public DataPermission getDataPermission(String mapperId) { |
| | | // 检查缓存中是否包含映射语句 ID 对应的 DataPermission 注解对象 |
| | | if (dataPermissionCacheMap.containsKey(mapperId)) { |
| | | return dataPermissionCacheMap.get(mapperId); |
| | | } |
| | | // 如果缓存中不包含映射语句 ID 对应的 DataPermission 注解对象,则尝试使用类名作为键查找 |
| | | String clazzName = mapperId.substring(0, mapperId.lastIndexOf(".")); |
| | | if (dataPermissionCacheMap.containsKey(clazzName)) { |
| | | return dataPermissionCacheMap.get(clazzName); |
| | |
| | | } |
| | | |
| | | /** |
| | | * 是否无效 |
| | | * 检查给定的映射语句 ID 是否有效,即是否能够找到对应的 DataPermission 注解对象 |
| | | * |
| | | * @param mapperId 映射语句 ID |
| | | * @return 如果找到对应的 DataPermission 注解对象,则返回 false;否则返回 true |
| | | */ |
| | | public boolean invalid(String mapperId) { |
| | | return getDataPermission(mapperId) == null; |
| | |
| | | |
| | | import cn.hutool.core.convert.Convert; |
| | | import com.baomidou.dynamic.datasource.DynamicRoutingDataSource; |
| | | import lombok.AccessLevel; |
| | | import lombok.NoArgsConstructor; |
| | | import org.dromara.common.core.exception.ServiceException; |
| | | import org.dromara.common.core.utils.SpringUtils; |
| | | import org.dromara.common.mybatis.enums.DataBaseType; |
| | | import lombok.AccessLevel; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import javax.sql.DataSource; |
| | | import java.sql.Connection; |
| | |
| | | import java.sql.SQLException; |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | import java.util.Set; |
| | | |
| | | /** |
| | | * 数据库助手 |
| | |
| | | |
| | | private static final String DATA_PERMISSION_KEY = "data:permission"; |
| | | |
| | | /** |
| | | * 从上下文中获取指定键的变量值,并将其转换为指定的类型 |
| | | * |
| | | * @param key 变量的键 |
| | | * @param <T> 变量值的类型 |
| | | * @return 指定键的变量值,如果不存在则返回 null |
| | | */ |
| | | public static <T> T getVariable(String key) { |
| | | Map<String, Object> context = getContext(); |
| | | return (T) context.get(key); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 向上下文中设置指定键的变量值 |
| | | * |
| | | * @param key 要设置的变量的键 |
| | | * @param value 要设置的变量值 |
| | | */ |
| | | public static void setVariable(String key, Object value) { |
| | | Map<String, Object> context = getContext(); |
| | | context.put(key, value); |
| | | } |
| | | |
| | | /** |
| | | * 获取数据权限上下文 |
| | | * |
| | | * @return 存储在SaStorage中的Map对象,用于存储数据权限相关的上下文信息 |
| | | * @throws NullPointerException 如果数据权限上下文类型异常,则抛出NullPointerException |
| | | */ |
| | | public static Map<String, Object> getContext() { |
| | | SaStorage saStorage = SaHolder.getStorage(); |
| | | Object attribute = saStorage.get(DATA_PERMISSION_KEY); |
| | |
| | | |
| | | /** |
| | | * 在忽略数据权限中执行 |
| | | * <p>禁止在忽略数据权限中执行忽略数据权限</p> |
| | | * |
| | | * @param handle 处理执行方法 |
| | | */ |
| | |
| | | |
| | | /** |
| | | * 在忽略数据权限中执行 |
| | | * <p>禁止在忽略数据权限中执行忽略数据权限</p> |
| | | * |
| | | * @param handle 处理执行方法 |
| | | */ |
| | |
| | | |
| | | private final PlusDataPermissionHandler dataPermissionHandler; |
| | | |
| | | /** |
| | | * 构造函数,初始化 PlusDataPermissionHandler 实例 |
| | | * |
| | | * @param mapperPackage 扫描的映射器包 |
| | | */ |
| | | public PlusDataPermissionInterceptor(String mapperPackage) { |
| | | this.dataPermissionHandler = new PlusDataPermissionHandler(mapperPackage); |
| | | } |
| | | |
| | | /** |
| | | * 在执行查询之前,检查并处理数据权限相关逻辑 |
| | | * |
| | | * @param executor MyBatis 执行器对象 |
| | | * @param ms 映射语句对象 |
| | | * @param parameter 方法参数 |
| | | * @param rowBounds 分页对象 |
| | | * @param resultHandler 结果处理器 |
| | | * @param boundSql 绑定的 SQL 对象 |
| | | * @throws SQLException 如果发生 SQL 异常 |
| | | */ |
| | | @Override |
| | | public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { |
| | | // 检查忽略注解 |
| | | // 检查是否需要忽略数据权限处理 |
| | | if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) { |
| | | return; |
| | | } |
| | | // 检查是否无效 无数据权限注解 |
| | | // 检查是否缺少有效的数据权限注解 |
| | | if (dataPermissionHandler.invalid(ms.getId())) { |
| | | return; |
| | | } |
| | |
| | | mpBs.sql(parserSingle(mpBs.sql(), ms.getId())); |
| | | } |
| | | |
| | | /** |
| | | * 在准备 SQL 语句之前,检查并处理更新和删除操作的数据权限相关逻辑 |
| | | * |
| | | * @param sh MyBatis StatementHandler 对象 |
| | | * @param connection 数据库连接对象 |
| | | * @param transactionTimeout 事务超时时间 |
| | | */ |
| | | @Override |
| | | public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) { |
| | | PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh); |
| | | MappedStatement ms = mpSh.mappedStatement(); |
| | | // 获取 SQL 命令类型(增、删、改、查) |
| | | SqlCommandType sct = ms.getSqlCommandType(); |
| | | |
| | | // 只处理更新和删除操作的 SQL 语句 |
| | | if (sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) { |
| | | if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) { |
| | | return; |
| | | } |
| | | // 检查是否无效 无数据权限注解 |
| | | // 检查是否缺少有效的数据权限注解 |
| | | if (dataPermissionHandler.invalid(ms.getId())) { |
| | | return; |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 处理 SELECT 查询语句中的 WHERE 条件 |
| | | * |
| | | * @param select SELECT 查询对象 |
| | | * @param index 查询语句的索引 |
| | | * @param sql 查询语句 |
| | | * @param obj WHERE 条件参数 |
| | | */ |
| | | @Override |
| | | protected void processSelect(Select select, int index, String sql, Object obj) { |
| | | if (select instanceof PlainSelect) { |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 处理 UPDATE 语句中的 WHERE 条件 |
| | | * |
| | | * @param update UPDATE 查询对象 |
| | | * @param index 查询语句的索引 |
| | | * @param sql 查询语句 |
| | | * @param obj WHERE 条件参数 |
| | | */ |
| | | @Override |
| | | protected void processUpdate(Update update, int index, String sql, Object obj) { |
| | | Expression sqlSegment = dataPermissionHandler.getSqlSegment(update.getWhere(), (String) obj, false); |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 处理 DELETE 语句中的 WHERE 条件 |
| | | * |
| | | * @param delete DELETE 查询对象 |
| | | * @param index 查询语句的索引 |
| | | * @param sql 查询语句 |
| | | * @param obj WHERE 条件参数 |
| | | */ |
| | | @Override |
| | | protected void processDelete(Delete delete, int index, String sql, Object obj) { |
| | | Expression sqlSegment = dataPermissionHandler.getSqlSegment(delete.getWhere(), (String) obj, false); |
| | |
| | | } |
| | | |
| | | /** |
| | | * 设置 where 条件 |
| | | * 设置 SELECT 语句的 WHERE 条件 |
| | | * |
| | | * @param plainSelect 查询对象 |
| | | * @param mappedStatementId 执行方法id |
| | | * @param plainSelect SELECT 查询对象 |
| | | * @param mappedStatementId 映射语句的 ID |
| | | */ |
| | | protected void setWhere(PlainSelect plainSelect, String mappedStatementId) { |
| | | Expression sqlSegment = dataPermissionHandler.getSqlSegment(plainSelect.getWhere(), mappedStatementId, true); |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 构建表达式,用于处理表的数据权限 |
| | | * |
| | | * @param table 表对象 |
| | | * @param where WHERE 条件表达式 |
| | | * @param whereSegment WHERE 条件片段 |
| | | * @return 构建的表达式 |
| | | */ |
| | | @Override |
| | | public Expression buildTableExpression(Table table, Expression where, String whereSegment) { |
| | | // 只有新版数据权限处理器才会执行到这里 |
| | |
| | | # 是否过滤 Log |
| | | filter=true |
| | | # 过滤 Log 时所排除的 sql 关键字,以逗号分隔 |
| | | exclude=SELECT 1 |
| | | exclude= |
| | |
| | | .key(key) |
| | | .contentMD5(StringUtils.isNotEmpty(md5Digest) ? md5Digest : null) |
| | | .contentType(contentType) |
| | | .acl(getAccessPolicy().getObjectCannedACL()) |
| | | .build()) |
| | | .addTransferListener(LoggingTransferListener.create()) |
| | | .source(filePath).build()); |
| | |
| | | y -> y.bucket(properties.getBucketName()) |
| | | .key(key) |
| | | .contentType(contentType) |
| | | .acl(getAccessPolicy().getObjectCannedACL()) |
| | | .build()) |
| | | .build()); |
| | | |
| | |
| | | package org.dromara.common.redis.utils; |
| | | |
| | | import org.dromara.common.core.utils.SpringUtils; |
| | | import lombok.AccessLevel; |
| | | import lombok.NoArgsConstructor; |
| | | import org.dromara.common.core.utils.SpringUtils; |
| | | import org.redisson.api.*; |
| | | |
| | | import java.util.concurrent.CompletionStage; |
| | | import java.util.concurrent.TimeUnit; |
| | | import java.util.function.Consumer; |
| | | import java.util.function.Function; |
| | | |
| | | /** |
| | | * 分布式队列工具 |
| | |
| | | /** |
| | | * 订阅阻塞队列(可订阅所有实现类 例如: 延迟 优先 有界 等) |
| | | */ |
| | | public static <T> void subscribeBlockingQueue(String queueName, Consumer<T> consumer, boolean isDelayed) { |
| | | public static <T> void subscribeBlockingQueue(String queueName, Function<T, CompletionStage<Void>> consumer, boolean isDelayed) { |
| | | RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName); |
| | | if (isDelayed) { |
| | | // 订阅延迟队列 |
| | |
| | | import cn.dev33.satoken.session.SaSession; |
| | | import cn.dev33.satoken.stp.SaLoginModel; |
| | | import cn.dev33.satoken.stp.StpUtil; |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.convert.Convert; |
| | | import cn.hutool.core.util.ObjectUtil; |
| | | import lombok.AccessLevel; |
| | |
| | | } |
| | | |
| | | /** |
| | | * 获取用户账户 |
| | | */ |
| | | public static String getUsername() { |
| | | return Convert.toStr(getExtra(USER_NAME_KEY)); |
| | | } |
| | | |
| | | /** |
| | | * 获取租户ID |
| | | */ |
| | | public static String getTenantId() { |
| | |
| | | } |
| | | |
| | | /** |
| | | * 获取用户账户 |
| | | */ |
| | | public static String getUsername() { |
| | | return getLoginUser().getUsername(); |
| | | } |
| | | |
| | | /** |
| | | * 获取用户类型 |
| | | */ |
| | | public static UserType getUserType() { |
| | |
| | | * @return 结果 |
| | | */ |
| | | public static boolean isTenantAdmin(Set<String> rolePermission) { |
| | | if (CollUtil.isEmpty(rolePermission)) { |
| | | return false; |
| | | } |
| | | return rolePermission.contains(TenantConstants.TENANT_ADMIN_ROLE_KEY); |
| | | } |
| | | |
| | |
| | | * @return 结果 |
| | | */ |
| | | public static boolean isLogin() { |
| | | return getLoginUser() != null; |
| | | try { |
| | | return getLoginUser() != null; |
| | | } catch (Exception e) { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | } |
| | |
| | | @AutoConfiguration |
| | | public class UndertowConfig implements WebServerFactoryCustomizer<UndertowServletWebServerFactory> { |
| | | |
| | | /** |
| | | * 设置 Undertow 的 websocket 缓冲池 |
| | | */ |
| | | @Override |
| | | public void customize(UndertowServletWebServerFactory factory) { |
| | | // 默认不直接分配内存 如果项目中使用了 websocket 建议直接分配 |
| | | factory.addDeploymentInfoCustomizers(deploymentInfo -> { |
| | | WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo(); |
| | | webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(false, 512)); |
| | | webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(true, 1024)); |
| | | deploymentInfo.addServletContextAttribute("io.undertow.websockets.jsr.WebSocketDeploymentInfo", webSocketDeploymentInfo); |
| | | // 使用虚拟线程 |
| | | if (SpringUtils.isVirtual()) { |
| | |
| | | */ |
| | | @Override |
| | | public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) { |
| | | // 检查是否登录 是否有token |
| | | LoginUser loginUser = LoginHelper.getLoginUser(); |
| | | try { |
| | | // 检查是否登录 是否有token |
| | | LoginUser loginUser = LoginHelper.getLoginUser(); |
| | | |
| | | // 解决 ws 不走 mvc 拦截器问题(cloud 版本不受影响) |
| | | // 检查 header 与 param 里的 clientid 与 token 里的是否一致 |
| | | String headerCid = ServletUtils.getRequest().getHeader(LoginHelper.CLIENT_KEY); |
| | | String paramCid = ServletUtils.getParameter(LoginHelper.CLIENT_KEY); |
| | | String clientId = StpUtil.getExtra(LoginHelper.CLIENT_KEY).toString(); |
| | | if (!StringUtils.equalsAny(clientId, headerCid, paramCid)) { |
| | | // token 无效 |
| | | throw NotLoginException.newInstance(StpUtil.getLoginType(), |
| | | "-100", "客户端ID与Token不匹配", |
| | | StpUtil.getTokenValue()); |
| | | // 解决 ws 不走 mvc 拦截器问题(cloud 版本不受影响) |
| | | // 检查 header 与 param 里的 clientid 与 token 里的是否一致 |
| | | String headerCid = ServletUtils.getRequest().getHeader(LoginHelper.CLIENT_KEY); |
| | | String paramCid = ServletUtils.getParameter(LoginHelper.CLIENT_KEY); |
| | | String clientId = StpUtil.getExtra(LoginHelper.CLIENT_KEY).toString(); |
| | | if (!StringUtils.equalsAny(clientId, headerCid, paramCid)) { |
| | | // token 无效 |
| | | throw NotLoginException.newInstance(StpUtil.getLoginType(), |
| | | "-100", "客户端ID与Token不匹配", |
| | | StpUtil.getTokenValue()); |
| | | } |
| | | |
| | | attributes.put(LOGIN_USER_KEY, loginUser); |
| | | return true; |
| | | } catch (NotLoginException e) { |
| | | log.error("WebSocket 认证失败'{}',无法访问系统资源", e.getMessage()); |
| | | return false; |
| | | } |
| | | |
| | | attributes.put(LOGIN_USER_KEY, loginUser); |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | |
| | | # 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/ |
| | | FROM bellsoft/liberica-openjdk-alpine:17.0.11-cds |
| | | #FROM bellsoft/liberica-openjdk-alpine:21.0.3-cds |
| | | #FROM findepi/graalvm:java17-native |
| | | FROM openjdk:17.0.2-oraclelinux8 |
| | | |
| | | MAINTAINER Lion Li |
| | | LABEL maintainer="Lion Li" |
| | | |
| | | RUN mkdir -p /ruoyi/monitor/logs |
| | | |
| | |
| | | # 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/ |
| | | FROM bellsoft/liberica-openjdk-alpine:17.0.11-cds |
| | | #FROM bellsoft/liberica-openjdk-alpine:21.0.3-cds |
| | | #FROM findepi/graalvm:java17-native |
| | | FROM openjdk:17.0.2-oraclelinux8 |
| | | |
| | | MAINTAINER Lion Li |
| | | LABEL maintainer="Lion Li" |
| | | |
| | | RUN mkdir -p /ruoyi/snailjob/logs |
| | | |
| | |
| | | ENV LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS="-Xms512m -Xmx1024m" |
| | | |
| | | EXPOSE 8800 |
| | | EXPOSE 1788 |
| | | EXPOSE 17888 |
| | | |
| | | ADD ./target/ruoyi-snailjob-server.jar ./app.jar |
| | | |
| | |
| | | # 拉取重试数据的每批次的大小 |
| | | job-pull-page-size: 1000 |
| | | # 服务端netty端口 |
| | | netty-port: 1788 |
| | | netty-port: 17888 |
| | | # 一个客户端每秒最多接收的重试数量指令 |
| | | limiter: 1000 |
| | | # 号段模式下步长配置 |
| | |
| | | # 拉取重试数据的每批次的大小 |
| | | job-pull-page-size: 1000 |
| | | # 服务端 netty 端口 |
| | | netty-port: 1788 |
| | | netty-port: 17888 |
| | | # 一个客户端每秒最多接收的重试数量指令 |
| | | limiter: 1000 |
| | | # 号段模式下步长配置 |
| | |
| | | package org.dromara.demo.controller.queue; |
| | | |
| | | import cn.dev33.satoken.annotation.SaIgnore; |
| | | import org.dromara.common.core.domain.R; |
| | | import org.dromara.common.redis.utils.QueueUtils; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.dromara.common.core.domain.R; |
| | | import org.dromara.common.redis.utils.QueueUtils; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.concurrent.CompletableFuture; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | /** |
| | |
| | | QueueUtils.subscribeBlockingQueue(queueName, (String orderNum) -> { |
| | | // 观察接收时间 |
| | | log.info("通道: {}, 收到数据: {}", queueName, orderNum); |
| | | return CompletableFuture.runAsync(() -> { |
| | | // 异步处理数据逻辑 不要在上方处理业务逻辑 |
| | | log.info("数据处理: {}", orderNum); |
| | | }); |
| | | }, true); |
| | | return R.ok("操作成功"); |
| | | } |
| | |
| | | @DataColumn(key = "deptName", value = "dept_id"), |
| | | @DataColumn(key = "userName", value = "user_id") |
| | | }) |
| | | int deleteBatchIds(@Param(Constants.COLL) Collection<?> idList); |
| | | int deleteByIds(@Param(Constants.COLL) Collection<?> idList); |
| | | } |
| | |
| | | if (isValid) { |
| | | //TODO 做一些业务上的校验,判断是否需要校验 |
| | | } |
| | | return baseMapper.deleteBatchIds(ids) > 0; |
| | | return baseMapper.deleteByIds(ids) > 0; |
| | | } |
| | | |
| | | @Override |
| | |
| | | if (isValid) { |
| | | //TODO 做一些业务上的校验,判断是否需要校验 |
| | | } |
| | | return baseMapper.deleteBatchIds(ids) > 0; |
| | | return baseMapper.deleteByIds(ids) > 0; |
| | | } |
| | | } |
| | |
| | | @Override |
| | | public void deleteGenTableByIds(Long[] tableIds) { |
| | | List<Long> ids = Arrays.asList(tableIds); |
| | | baseMapper.deleteBatchIds(ids); |
| | | baseMapper.deleteByIds(ids); |
| | | genTableColumnMapper.delete(new LambdaQueryWrapper<GenTableColumn>().in(GenTableColumn::getTableId, ids)); |
| | | } |
| | | |
| | |
| | | if (CollUtil.isNotEmpty(delColumns)) { |
| | | List<Long> ids = StreamUtils.toList(delColumns, GenTableColumn::getColumnId); |
| | | if (CollUtil.isNotEmpty(ids)) { |
| | | genTableColumnMapper.deleteBatchIds(ids); |
| | | genTableColumnMapper.deleteByIds(ids); |
| | | } |
| | | } |
| | | } |
| | |
| | | if(isValid){ |
| | | //TODO 做一些业务上的校验,判断是否需要校验 |
| | | } |
| | | return baseMapper.deleteBatchIds(ids) > 0; |
| | | return baseMapper.deleteByIds(ids) > 0; |
| | | } |
| | | } |
| | |
| | | public R<Void> add(@Validated @RequestBody SysDeptBo dept) { |
| | | if (!deptService.checkDeptNameUnique(dept)) { |
| | | return R.fail("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在"); |
| | | } else if (StringUtils.isNotBlank(dept.getDeptCategory()) && !deptService.checkDeptCategoryUnique(dept)) { |
| | | return R.fail("新增部门'" + dept.getDeptName() + "'失败,部门类别编码已存在"); |
| | | } |
| | | return toAjax(deptService.insertDept(dept)); |
| | | } |
| | |
| | | deptService.checkDeptDataScope(deptId); |
| | | if (!deptService.checkDeptNameUnique(dept)) { |
| | | return R.fail("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在"); |
| | | } else if (StringUtils.isNotBlank(dept.getDeptCategory()) && !deptService.checkDeptCategoryUnique(dept)) { |
| | | return R.fail("修改部门'" + dept.getDeptName() + "'失败,部门类别编码已存在"); |
| | | } else if (dept.getParentId().equals(deptId)) { |
| | | return R.fail("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己"); |
| | | } else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus())) { |
| | |
| | | public interface SysMenuMapper extends BaseMapperPlus<SysMenu, SysMenuVo> { |
| | | |
| | | /** |
| | | * 根据用户所有权限 |
| | | * |
| | | * @return 权限列表 |
| | | */ |
| | | List<String> selectMenuPerms(); |
| | | |
| | | /** |
| | | * 根据用户查询系统菜单列表 |
| | | * |
| | | * @param queryWrapper 查询条件 |
| | |
| | | boolean checkDeptNameUnique(SysDeptBo dept); |
| | | |
| | | /** |
| | | * 校验部门类别编码是否唯一 |
| | | * |
| | | * @param dept 部门信息 |
| | | * @return 结果 |
| | | */ |
| | | boolean checkDeptCategoryUnique(SysDeptBo dept); |
| | | |
| | | /** |
| | | * 校验部门是否有数据权限 |
| | | * |
| | | * @param deptId 部门id |
| | |
| | | if (isValid) { |
| | | //TODO 做一些业务上的校验,判断是否需要校验 |
| | | } |
| | | return baseMapper.deleteBatchIds(ids) > 0; |
| | | return baseMapper.deleteByIds(ids) > 0; |
| | | } |
| | | } |
| | |
| | | } |
| | | CacheUtils.evict(CacheNames.SYS_CONFIG, config.getConfigKey()); |
| | | } |
| | | baseMapper.deleteBatchIds(Arrays.asList(configIds)); |
| | | baseMapper.deleteByIds(Arrays.asList(configIds)); |
| | | } |
| | | |
| | | /** |
| | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.convert.Convert; |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import org.dromara.system.domain.SysDept; |
| | | import org.dromara.common.mybatis.helper.DataBaseHelper; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.dromara.common.core.utils.StreamUtils; |
| | | import org.dromara.common.mybatis.helper.DataBaseHelper; |
| | | import org.dromara.system.domain.SysDept; |
| | | import org.dromara.system.domain.SysRoleDept; |
| | | import org.dromara.system.mapper.SysDeptMapper; |
| | | import org.dromara.system.mapper.SysRoleDeptMapper; |
| | | import org.dromara.system.service.ISysDataScopeService; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.List; |
| | |
| | | private final SysRoleDeptMapper roleDeptMapper; |
| | | private final SysDeptMapper deptMapper; |
| | | |
| | | /** |
| | | * 获取角色自定义权限 |
| | | * |
| | | * @param roleId 角色Id |
| | | * @return 部门Id组 |
| | | */ |
| | | @Override |
| | | public String getRoleCustom(Long roleId) { |
| | | List<SysRoleDept> list = roleDeptMapper.selectList( |
| | |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * 获取部门及以下权限 |
| | | * |
| | | * @param deptId 部门Id |
| | | * @return 部门Id组 |
| | | */ |
| | | @Override |
| | | public String getDeptAndChild(Long deptId) { |
| | | List<SysDept> deptList = deptMapper.selectList(new LambdaQueryWrapper<SysDept>() |
| | |
| | | } |
| | | |
| | | /** |
| | | * 校验部门类别编码是否唯一 |
| | | * |
| | | * @param dept 部门信息 |
| | | * @return 结果 |
| | | */ |
| | | @Override |
| | | public boolean checkDeptCategoryUnique(SysDeptBo dept) { |
| | | boolean exist = baseMapper.exists(new LambdaQueryWrapper<SysDept>() |
| | | .eq(SysDept::getDeptCategory, dept.getDeptCategory()) |
| | | .ne(ObjectUtil.isNotNull(dept.getDeptId()), SysDept::getDeptId, dept.getDeptId())); |
| | | return !exist; |
| | | } |
| | | |
| | | /** |
| | | * 校验部门是否有数据权限 |
| | | * |
| | | * @param deptId 部门id |
| | |
| | | } |
| | | CacheUtils.evict(CacheNames.SYS_DICT, dictType.getDictType()); |
| | | } |
| | | baseMapper.deleteBatchIds(Arrays.asList(dictIds)); |
| | | baseMapper.deleteByIds(Arrays.asList(dictIds)); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | @Override |
| | | public int deleteLogininforByIds(Long[] infoIds) { |
| | | return baseMapper.deleteBatchIds(Arrays.asList(infoIds)); |
| | | return baseMapper.deleteByIds(Arrays.asList(infoIds)); |
| | | } |
| | | |
| | | /** |
| | |
| | | .orderByAsc(SysMenu::getOrderNum)); |
| | | } else { |
| | | QueryWrapper<SysMenu> wrapper = Wrappers.query(); |
| | | wrapper.eq("sur.user_id", userId) |
| | | wrapper.inSql("r.role_id", "select role_id from sys_user_role where user_id = " + userId) |
| | | .like(StringUtils.isNotBlank(menu.getMenuName()), "m.menu_name", menu.getMenuName()) |
| | | .eq(StringUtils.isNotBlank(menu.getVisible()), "m.visible", menu.getVisible()) |
| | | .eq(StringUtils.isNotBlank(menu.getStatus()), "m.status", menu.getStatus()) |
| | |
| | | |
| | | /** |
| | | * 构建前端路由所需要的菜单 |
| | | * 路由name命名规则 path首字母转大写 + id |
| | | * |
| | | * @param menus 菜单列表 |
| | | * @return 路由列表 |
| | |
| | | public List<RouterVo> buildMenus(List<SysMenu> menus) { |
| | | List<RouterVo> routers = new LinkedList<>(); |
| | | for (SysMenu menu : menus) { |
| | | String name = menu.getRouteName() + menu.getMenuId(); |
| | | RouterVo router = new RouterVo(); |
| | | router.setHidden("1".equals(menu.getVisible())); |
| | | router.setName(menu.getRouteName()); |
| | | router.setName(name); |
| | | router.setPath(menu.getRouterPath()); |
| | | router.setComponent(menu.getComponentInfo()); |
| | | router.setQuery(menu.getQueryParam()); |
| | |
| | | router.setRedirect("noRedirect"); |
| | | router.setChildren(buildMenus(cMenus)); |
| | | } else if (menu.isMenuFrame()) { |
| | | String frameName = StringUtils.capitalize(menu.getPath()) + menu.getMenuId(); |
| | | router.setMeta(null); |
| | | List<RouterVo> childrenList = new ArrayList<>(); |
| | | RouterVo children = new RouterVo(); |
| | | children.setPath(menu.getPath()); |
| | | children.setComponent(menu.getComponent()); |
| | | children.setName(StringUtils.capitalize(menu.getPath())); |
| | | children.setName(frameName); |
| | | children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath())); |
| | | children.setQuery(menu.getQueryParam()); |
| | | childrenList.add(children); |
| | |
| | | List<RouterVo> childrenList = new ArrayList<>(); |
| | | RouterVo children = new RouterVo(); |
| | | String routerPath = SysMenu.innerLinkReplaceEach(menu.getPath()); |
| | | String innerLinkName = StringUtils.capitalize(routerPath) + menu.getMenuId(); |
| | | children.setPath(routerPath); |
| | | children.setComponent(UserConstants.INNER_LINK); |
| | | children.setName(StringUtils.capitalize(routerPath)); |
| | | children.setName(innerLinkName); |
| | | children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), menu.getPath())); |
| | | childrenList.add(children); |
| | | router.setChildren(childrenList); |
| | |
| | | */ |
| | | @Override |
| | | public int deleteNoticeByIds(Long[] noticeIds) { |
| | | return baseMapper.deleteBatchIds(Arrays.asList(noticeIds)); |
| | | return baseMapper.deleteByIds(Arrays.asList(noticeIds)); |
| | | } |
| | | } |
| | |
| | | */ |
| | | @Override |
| | | public int deleteOperLogByIds(Long[] operIds) { |
| | | return baseMapper.deleteBatchIds(Arrays.asList(operIds)); |
| | | return baseMapper.deleteByIds(Arrays.asList(operIds)); |
| | | } |
| | | |
| | | /** |
| | |
| | | SysOssConfig config = baseMapper.selectById(configId); |
| | | list.add(config); |
| | | } |
| | | boolean flag = baseMapper.deleteBatchIds(ids) > 0; |
| | | boolean flag = baseMapper.deleteByIds(ids) > 0; |
| | | if (flag) { |
| | | list.forEach(sysOssConfig -> |
| | | CacheUtils.evict(CacheNames.SYS_OSS_CONFIG, sysOssConfig.getConfigKey())); |
| | |
| | | OssClient storage = OssFactory.instance(sysOss.getService()); |
| | | storage.delete(sysOss.getUrl()); |
| | | } |
| | | return baseMapper.deleteBatchIds(ids) > 0; |
| | | return baseMapper.deleteByIds(ids) > 0; |
| | | } |
| | | |
| | | /** |
| | |
| | | throw new ServiceException(String.format("%1$s已分配,不能删除!", post.getPostName())); |
| | | } |
| | | } |
| | | return baseMapper.deleteBatchIds(Arrays.asList(postIds)); |
| | | return baseMapper.deleteByIds(Arrays.asList(postIds)); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | @Override |
| | | public List<SysRoleVo> selectRolesAuthByUserId(Long userId) { |
| | | List<SysRoleVo> userRoles = baseMapper.selectRolePermissionByUserId(userId); |
| | | List<SysRoleVo> userRoles = baseMapper.selectRolesByUserId(userId); |
| | | List<SysRoleVo> roles = selectRoleAll(); |
| | | // 使用HashSet提高查找效率 |
| | | Set<Long> userRoleIds = StreamUtils.toSet(userRoles, SysRoleVo::getRoleId); |
| | | for (SysRoleVo role : roles) { |
| | | for (SysRoleVo userRole : userRoles) { |
| | | if (role.getRoleId().longValue() == userRole.getRoleId().longValue()) { |
| | | role.setFlag(true); |
| | | break; |
| | | } |
| | | if (userRoleIds.contains(role.getRoleId())) { |
| | | role.setFlag(true); |
| | | } |
| | | } |
| | | return roles; |
| | |
| | | */ |
| | | @Override |
| | | public Set<String> selectRolePermissionByUserId(Long userId) { |
| | | List<SysRoleVo> perms = baseMapper.selectRolePermissionByUserId(userId); |
| | | List<SysRoleVo> perms = baseMapper.selectRolesByUserId(userId); |
| | | Set<String> permsSet = new HashSet<>(); |
| | | for (SysRoleVo perm : perms) { |
| | | if (ObjectUtil.isNotNull(perm)) { |
| | |
| | | roleMenuMapper.delete(new LambdaQueryWrapper<SysRoleMenu>().in(SysRoleMenu::getRoleId, ids)); |
| | | // 删除角色与部门关联 |
| | | roleDeptMapper.delete(new LambdaQueryWrapper<SysRoleDept>().in(SysRoleDept::getRoleId, ids)); |
| | | return baseMapper.deleteBatchIds(ids); |
| | | return baseMapper.deleteByIds(ids); |
| | | } |
| | | |
| | | /** |
| | |
| | | throw new ServiceException("租户套餐已被使用"); |
| | | } |
| | | } |
| | | return baseMapper.deleteBatchIds(ids) > 0; |
| | | return baseMapper.deleteByIds(ids) > 0; |
| | | } |
| | | } |
| | |
| | | throw new ServiceException("超管租户不能删除"); |
| | | } |
| | | } |
| | | return baseMapper.deleteBatchIds(ids) > 0; |
| | | return baseMapper.deleteByIds(ids) > 0; |
| | | } |
| | | |
| | | /** |
| | |
| | | w.in("u.dept_id", ids); |
| | | }).orderByAsc("u.user_id"); |
| | | if (StringUtils.isNotBlank(user.getExcludeUserIds())) { |
| | | wrapper.notIn("u.user_id", StringUtils.splitList(user.getExcludeUserIds())); |
| | | wrapper.notIn("u.user_id", StringUtils.splitTo(user.getExcludeUserIds(), Convert::toLong)); |
| | | } |
| | | return wrapper; |
| | | } |
| | |
| | | // 删除用户与岗位表 |
| | | userPostMapper.delete(new LambdaQueryWrapper<SysUserPost>().in(SysUserPost::getUserId, ids)); |
| | | // 防止更新失败导致的数据删除 |
| | | int flag = baseMapper.deleteBatchIds(ids); |
| | | int flag = baseMapper.deleteByIds(ids); |
| | | if (flag < 1) { |
| | | throw new ServiceException("删除用户失败!"); |
| | | } |
| | |
| | | select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.query_param, m.visible, m.status, |
| | | m.perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time |
| | | from sys_menu m |
| | | left join sys_role_menu rm on m.menu_id = rm.menu_id |
| | | left join sys_user_role sur on rm.role_id = sur.role_id |
| | | left join sys_role ro on sur.role_id = ro.role_id |
| | | left join sys_role_menu rm on m.menu_id = rm.menu_id |
| | | left join sys_role r on rm.role_id = r.role_id |
| | | ${ew.getCustomSqlSegment} |
| | | </select> |
| | | |
| | |
| | | m.order_num, |
| | | m.create_time |
| | | from sys_menu m |
| | | left join sys_role_menu rm on m.menu_id = rm.menu_id |
| | | left join sys_user_role sur on rm.role_id = sur.role_id |
| | | left join sys_role ro on sur.role_id = ro.role_id |
| | | left join sys_user u on sur.user_id = u.user_id |
| | | where u.user_id = #{userId} |
| | | and m.menu_type in ('M', 'C') |
| | | and m.status = '0' |
| | | and ro.status = '0' |
| | | left join sys_role_menu rm on m.menu_id = rm.menu_id and m.status = '0' |
| | | left join sys_role r on rm.role_id = r.role_id and r.status = '0' |
| | | where m.menu_type in ('M', 'C') |
| | | and r.role_id in (select role_id from sys_user_role where user_id = #{userId}) |
| | | order by m.parent_id, m.order_num |
| | | </select> |
| | | |
| | |
| | | order by m.parent_id, m.order_num |
| | | </select> |
| | | |
| | | <select id="selectMenuPerms" resultType="String"> |
| | | select distinct m.perms |
| | | from sys_menu m |
| | | left join sys_role_menu rm on m.menu_id = rm.menu_id |
| | | left join sys_user_role sur on rm.role_id = sur.role_id |
| | | </select> |
| | | |
| | | <select id="selectMenuPermsByUserId" parameterType="Long" resultType="String"> |
| | | select distinct m.perms |
| | | from sys_menu m |
| | | left join sys_role_menu rm on m.menu_id = rm.menu_id |
| | | left join sys_user_role sur on rm.role_id = sur.role_id |
| | | left join sys_role r on r.role_id = sur.role_id |
| | | where m.status = '0' |
| | | and r.status = '0' |
| | | and sur.user_id = #{userId} |
| | | left join sys_role_menu rm on m.menu_id = rm.menu_id and m.status = '0' |
| | | left join sys_role r on r.role_id = rm.role_id and r.status = '0' |
| | | where r.role_id in (select role_id from sys_user_role where user_id = #{userId}) |
| | | </select> |
| | | |
| | | <select id="selectMenuPermsByRoleId" parameterType="Long" resultType="String"> |
| | |
| | | r.data_scope, |
| | | r.status |
| | | from sys_role r |
| | | left join sys_user_role sur on sur.role_id = r.role_id |
| | | left join sys_user u on u.user_id = sur.user_id |
| | | WHERE r.del_flag = '0' and sur.user_id = #{userId} |
| | | WHERE r.del_flag = '0' and r.role_id in (select role_id from sys_user_role where user_id = #{userId}) |
| | | </select> |
| | | |
| | | <select id="selectRoleById" resultMap="SysRoleResult"> |
| | |
| | | |
| | | import cn.hutool.core.util.ObjectUtil; |
| | | import lombok.AllArgsConstructor; |
| | | import org.dromara.common.core.utils.StreamUtils; |
| | | import org.flowable.common.engine.impl.interceptor.Command; |
| | | import org.flowable.common.engine.impl.interceptor.CommandContext; |
| | | import org.flowable.engine.impl.persistence.entity.ExecutionEntity; |
| | |
| | | } |
| | | List<Long> userIdList = new ArrayList<>(); |
| | | userIds.forEach(e -> { |
| | | Long userId = assignees.stream().filter(id -> ObjectUtil.equals(id, e)).findFirst().orElse(null); |
| | | Long userId = StreamUtils.findFirst(assignees, id -> ObjectUtil.equals(id, e)); |
| | | if (userId == null) { |
| | | userIdList.add(e); |
| | | } |
| | |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.SneakyThrows; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.dromara.common.core.enums.BusinessStatusEnum; |
| | | import org.dromara.common.core.exception.ServiceException; |
| | | import org.dromara.common.core.service.UserService; |
| | | import org.dromara.common.core.utils.StreamUtils; |
| | |
| | | import org.dromara.common.mybatis.core.page.TableDataInfo; |
| | | import org.dromara.common.satoken.utils.LoginHelper; |
| | | import org.dromara.workflow.common.constant.FlowConstant; |
| | | import org.dromara.common.core.enums.BusinessStatusEnum; |
| | | import org.dromara.workflow.common.enums.TaskStatusEnum; |
| | | import org.dromara.workflow.domain.ActHiProcinst; |
| | | import org.dromara.workflow.domain.bo.ProcessInstanceBo; |
| | |
| | | import org.dromara.workflow.service.IWfTaskBackNodeService; |
| | | import org.dromara.workflow.utils.QueryUtils; |
| | | import org.dromara.workflow.utils.WorkflowUtils; |
| | | import org.flowable.bpmn.model.*; |
| | | import org.flowable.bpmn.model.BpmnModel; |
| | | import org.flowable.engine.*; |
| | | import org.flowable.engine.history.HistoricActivityInstance; |
| | | import org.flowable.engine.history.HistoricProcessInstance; |
| | |
| | | import java.nio.charset.StandardCharsets; |
| | | import java.util.List; |
| | | import java.util.*; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * 流程实例 服务层实现 |
| | |
| | | List<HistoricActivityInstance> highLightedFlowList = QueryUtils.hisActivityInstanceQuery(processInstanceId).orderByHistoricActivityInstanceStartTime().asc().list(); |
| | | for (HistoricActivityInstance tempActivity : highLightedFlowList) { |
| | | Map<String, Object> task = new HashMap<>(); |
| | | if (!FlowConstant.SEQUENCE_FLOW.equals(tempActivity.getActivityType()) && |
| | | !FlowConstant.PARALLEL_GATEWAY.equals(tempActivity.getActivityType()) && |
| | | !FlowConstant.EXCLUSIVE_GATEWAY.equals(tempActivity.getActivityType()) && |
| | | !FlowConstant.INCLUSIVE_GATEWAY.equals(tempActivity.getActivityType()) |
| | | ) { |
| | | task.put("key", tempActivity.getActivityId()); |
| | | task.put("completed", tempActivity.getEndTime() != null); |
| | | task.put("activityType", tempActivity.getActivityType()); |
| | | taskList.add(task); |
| | | switch (tempActivity.getActivityType()) { |
| | | case FlowConstant.SEQUENCE_FLOW, FlowConstant.PARALLEL_GATEWAY, |
| | | FlowConstant.EXCLUSIVE_GATEWAY, FlowConstant.INCLUSIVE_GATEWAY -> {} |
| | | default -> { |
| | | task.put("key", tempActivity.getActivityId()); |
| | | task.put("completed", tempActivity.getEndTime() != null); |
| | | task.put("activityType", tempActivity.getActivityType()); |
| | | taskList.add(task); |
| | | } |
| | | } |
| | | } |
| | | ProcessInstance processInstance = QueryUtils.instanceQuery(processInstanceId).singleResult(); |
| | | if (processInstance != null) { |
| | | taskList = taskList.stream().filter(e -> !e.get("activityType").equals(FlowConstant.END_EVENT)).collect(Collectors.toList()); |
| | | taskList = StreamUtils.filter(taskList, e -> !e.get("activityType").equals(FlowConstant.END_EVENT)); |
| | | } |
| | | //查询出运行中节点 |
| | | List<Map<String, Object>> runtimeNodeList = taskList.stream().filter(e -> !(Boolean) e.get("completed")).collect(Collectors.toList()); |
| | | List<Map<String, Object>> runtimeNodeList = StreamUtils.filter(taskList, e -> !(Boolean) e.get("completed")); |
| | | if (CollUtil.isNotEmpty(runtimeNodeList)) { |
| | | Iterator<Map<String, Object>> iterator = taskList.iterator(); |
| | | while (iterator.hasNext()) { |
| | |
| | | } |
| | | //附件 |
| | | if (CollUtil.isNotEmpty(attachmentList)) { |
| | | List<Attachment> attachments = attachmentList.stream().filter(e -> e.getTaskId().equals(historicTaskInstance.getId())).collect(Collectors.toList()); |
| | | List<Attachment> attachments = StreamUtils.filter(attachmentList, e -> e.getTaskId().equals(historicTaskInstance.getId())); |
| | | if (CollUtil.isNotEmpty(attachments)) { |
| | | actHistoryInfoVo.setAttachmentList(attachments); |
| | | } |
| | |
| | | String userId = String.valueOf(LoginHelper.getUserId()); |
| | | queryWrapper.eq("t.business_status_", BusinessStatusEnum.WAITING.getStatus()); |
| | | queryWrapper.eq(TenantHelper.isEnable(), "t.tenant_id_", TenantHelper.getTenantId()); |
| | | queryWrapper.and(w1 -> w1.eq("t.assignee_", userId).or(w2 -> w2.isNull("t.assignee_").apply("exists ( select LINK.ID_ from ACT_RU_IDENTITYLINK LINK where LINK.TASK_ID_ = t.ID_ and LINK.TYPE_ = 'candidate' " + "and (LINK.USER_ID_ = {0} or ( LINK.GROUP_ID_ IN " + getInParam(roleIds) + " ) ))", userId))); |
| | | String ids = StreamUtils.join(roleIds, x -> "'" + x + "'"); |
| | | queryWrapper.and(w1 -> w1.eq("t.assignee_", userId).or(w2 -> w2.isNull("t.assignee_").apply("exists ( select LINK.ID_ from ACT_RU_IDENTITYLINK LINK where LINK.TASK_ID_ = t.ID_ and LINK.TYPE_ = 'candidate' and (LINK.USER_ID_ = {0} or ( LINK.GROUP_ID_ IN ({1}) ) ))", userId, ids))); |
| | | if (StringUtils.isNotBlank(taskBo.getName())) { |
| | | queryWrapper.like("t.name_", taskBo.getName()); |
| | | } |
| | |
| | | } |
| | | } |
| | | return TableDataInfo.build(page); |
| | | } |
| | | |
| | | private String getInParam(List<String> param) { |
| | | StringBuilder sb = new StringBuilder(); |
| | | sb.append("("); |
| | | for (int i = 0; i < param.size(); i++) { |
| | | sb.append("'").append(param.get(i)).append("'"); |
| | | if (i != param.size() - 1) { |
| | | sb.append(","); |
| | | } |
| | | } |
| | | sb.append(")"); |
| | | return sb.toString(); |
| | | } |
| | | |
| | | /** |
| | |
| | | if (multiInstance == null && taskList.size() > 1) { |
| | | List<Task> tasks = StreamUtils.filter(taskList, e -> !e.getTaskDefinitionKey().equals(task.getTaskDefinitionKey())); |
| | | if (CollUtil.isNotEmpty(tasks)) { |
| | | actHiTaskinstMapper.deleteBatchIds(StreamUtils.toList(tasks, Task::getId)); |
| | | actHiTaskinstMapper.deleteByIds(StreamUtils.toList(tasks, Task::getId)); |
| | | } |
| | | } |
| | | |
| | |
| | | public Boolean deleteWithValidByIds(Collection<Long> ids) { |
| | | List<String> idList = StreamUtils.toList(ids, String::valueOf); |
| | | workflowService.deleteRunAndHisInstance(idList); |
| | | return baseMapper.deleteBatchIds(ids) > 0; |
| | | return baseMapper.deleteByIds(ids) > 0; |
| | | } |
| | | |
| | | /** |
| | |
| | | if (isValid) { |
| | | //TODO 做一些业务上的校验,判断是否需要校验 |
| | | } |
| | | return baseMapper.deleteBatchIds(ids) > 0; |
| | | return baseMapper.deleteByIds(ids) > 0; |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | @Override |
| | | public Boolean deleteByIds(Collection<Long> ids) { |
| | | return baseMapper.deleteBatchIds(ids) > 0; |
| | | return baseMapper.deleteByIds(ids) > 0; |
| | | } |
| | | |
| | | @Override |
| | |
| | | */ |
| | | @Override |
| | | public Boolean deleteByIds(Collection<Long> ids) { |
| | | return baseMapper.deleteBatchIds(ids) > 0; |
| | | return baseMapper.deleteByIds(ids) > 0; |
| | | } |
| | | } |
| | |
| | | */ |
| | | @Override |
| | | public Boolean deleteByIds(Collection<Long> ids) { |
| | | return baseMapper.deleteBatchIds(ids) > 0; |
| | | return baseMapper.deleteByIds(ids) > 0; |
| | | } |
| | | |
| | | |
| | |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.dromara.common.core.exception.ServiceException; |
| | | import org.dromara.common.core.utils.StreamUtils; |
| | | import org.dromara.common.core.utils.StringUtils; |
| | | import org.dromara.common.satoken.utils.LoginHelper; |
| | | import org.dromara.workflow.domain.WfTaskBackNode; |
| | |
| | | wfTaskBackNode.setOrderNo(0); |
| | | wfTaskBackNodeMapper.insert(wfTaskBackNode); |
| | | } else { |
| | | WfTaskBackNode taskNode = list.stream().filter(e -> e.getNodeId().equals(wfTaskBackNode.getNodeId()) && e.getOrderNo() == 0).findFirst().orElse(null); |
| | | WfTaskBackNode taskNode = StreamUtils.findFirst(list, e -> e.getNodeId().equals(wfTaskBackNode.getNodeId()) && e.getOrderNo() == 0); |
| | | if (ObjectUtil.isEmpty(taskNode)) { |
| | | wfTaskBackNode.setOrderNo(list.get(0).getOrderNo() + 1); |
| | | WfTaskBackNode node = getListByInstanceIdAndNodeId(wfTaskBackNode.getInstanceId(), wfTaskBackNode.getNodeId()); |
| | |
| | | } |
| | | } |
| | | if (CollUtil.isNotEmpty(ids)) { |
| | | wfTaskBackNodeMapper.deleteBatchIds(ids); |
| | | wfTaskBackNodeMapper.deleteByIds(ids); |
| | | } |
| | | } |
| | | return true; |
| | |
| | | network_mode: "host" |
| | | |
| | | ruoyi-server1: |
| | | image: ruoyi/ruoyi-server:5.2.0 |
| | | image: ruoyi/ruoyi-server:5.2.1 |
| | | container_name: ruoyi-server1 |
| | | environment: |
| | | # 时区上海 |
| | |
| | | network_mode: "host" |
| | | |
| | | ruoyi-server2: |
| | | image: ruoyi/ruoyi-server:5.2.0 |
| | | image: ruoyi/ruoyi-server:5.2.1 |
| | | container_name: ruoyi-server2 |
| | | environment: |
| | | # 时区上海 |
| | |
| | | network_mode: "host" |
| | | |
| | | ruoyi-monitor-admin: |
| | | image: ruoyi/ruoyi-monitor-admin:5.2.0 |
| | | image: ruoyi/ruoyi-monitor-admin:5.2.1 |
| | | container_name: ruoyi-monitor-admin |
| | | environment: |
| | | # 时区上海 |
| | |
| | | network_mode: "host" |
| | | |
| | | ruoyi-snailjob-server: |
| | | image: ruoyi/ruoyi-snailjob-server:5.2.0 |
| | | image: ruoyi/ruoyi-snailjob-server:5.2.1 |
| | | container_name: ruoyi-snailjob-server |
| | | environment: |
| | | # 时区上海 |
| | | TZ: Asia/Shanghai |
| | | ports: |
| | | - "8800:8800" |
| | | - "1788:1788" |
| | | - "17888:17888" |
| | | volumes: |
| | | - /docker/snailjob/logs/:/ruoyi/snailjob/logs |
| | | privileged: true |
| | |
| | | # } |
| | | |
| | | # 限制外网访问内网 actuator 相关路径 |
| | | location ~ ^(/[^/]*)?/actuator(/.*)?$ { |
| | | location ~ ^(/[^/]*)?/actuator.*(/.*)?$ { |
| | | return 403; |
| | | } |
| | | |
| | |
| | | ); |
| | | |
| | | comment on table test_leave is '请假申请表'; |
| | | |
| | | comment on column test_leave.id is '主键'; |
| | | |
| | | comment on column test_leave.leave_type is '请假类型'; |
| | | |
| | | comment on column test_leave.start_date is '开始时间'; |
| | | |
| | | comment on column test_leave.end_date is '结束时间'; |
| | | |
| | | comment on column test_leave.remark is '请假原因'; |
| | | |
| | | comment on column test_leave.status is '状态'; |
| | | |
| | | comment on column test_leave.create_dept is '创建部门'; |
| | | |
| | | comment on column test_leave.create_by is '创建者'; |
| | | |
| | | comment on column test_leave.create_time is '创建时间'; |
| | | |
| | | comment on column test_leave.update_by is '更新者'; |
| | | |
| | | comment on column test_leave.update_time is '更新时间'; |
| | | |
| | | comment on column test_leave.tenant_id is '租户编码'; |
| | | |
| | | alter table test_leave |
| | |
| | | ); |
| | | |
| | | comment on table wf_category is '流程分类'; |
| | | |
| | | comment on column wf_category.id is '主键'; |
| | | |
| | | comment on column wf_category.category_name is '分类名称'; |
| | | |
| | | comment on column wf_category.category_code is '分类编码'; |
| | | |
| | | comment on column wf_category.parent_id is '父级id'; |
| | | |
| | | comment on column wf_category.sort_num is '排序'; |
| | | |
| | | comment on column wf_category.tenant_id is '租户id'; |
| | | |
| | | comment on column wf_category.create_dept is '创建部门'; |
| | | |
| | | comment on column wf_category.create_by is '创建者'; |
| | | |
| | | comment on column wf_category.create_time is '创建时间'; |
| | | |
| | | comment on column wf_category.update_by is '修改者'; |
| | | |
| | | comment on column wf_category.update_time is '修改时间'; |
| | | |
| | | alter table wf_category |
| | |
| | | ); |
| | | |
| | | comment on table wf_task_back_node is '节点审批记录'; |
| | | |
| | | comment on column wf_task_back_node.id is '主键'; |
| | | |
| | | comment on column wf_task_back_node.node_id is '节点id'; |
| | | |
| | | comment on column wf_task_back_node.node_name is '节点名称'; |
| | | |
| | | comment on column wf_task_back_node.order_no is '排序'; |
| | | |
| | | comment on column wf_task_back_node.instance_id is '流程实例id'; |
| | | |
| | | comment on column wf_task_back_node.task_type is '节点类型'; |
| | | |
| | | comment on column wf_task_back_node.assignee is '审批人'; |
| | | |
| | | comment on column wf_task_back_node.tenant_id is '租户id'; |
| | | |
| | | comment on column wf_task_back_node.create_dept is '创建部门'; |
| | | |
| | | comment on column wf_task_back_node.create_by is '创建者'; |
| | | |
| | | comment on column wf_task_back_node.create_time is '创建时间'; |
| | | |
| | | comment on column wf_task_back_node.update_by is '修改者'; |
| | | |
| | | comment on column wf_task_back_node.update_time is '修改时间'; |
| | | |
| | | alter table wf_task_back_node |
| | |
| | | process_key varchar(255) not null, |
| | | version bigint not null, |
| | | tenant_id varchar(20), |
| | | remark varchar(500), |
| | | create_dept bigint, |
| | | create_by bigint, |
| | | create_time timestamp, |
| | |
| | | ); |
| | | |
| | | comment on table wf_definition_config is '流程定义配置'; |
| | | |
| | | comment on column wf_definition_config.id is '主键'; |
| | | |
| | | comment on column wf_definition_config.table_name is '表名'; |
| | | |
| | | comment on column wf_definition_config.definition_id is '流程定义ID'; |
| | | |
| | | comment on column wf_definition_config.process_key is '流程KEY'; |
| | | |
| | | comment on column wf_definition_config.version is '流程版本'; |
| | | |
| | | comment on column wf_definition_config.tenant_id is '租户id'; |
| | | |
| | | comment on column wf_definition_config.remark is '备注'; |
| | | comment on column wf_definition_config.create_dept is '创建部门'; |
| | | |
| | | comment on column wf_definition_config.create_by is '创建者'; |
| | | |
| | | comment on column wf_definition_config.create_time is '创建时间'; |
| | | |
| | | comment on column wf_definition_config.update_by is '修改者'; |
| | | |
| | | comment on column wf_definition_config.update_time is '修改时间'; |
| | | |
| | | alter table wf_definition_config |
| | |
| | | ); |
| | | |
| | | comment on table wf_form_manage is '表单管理'; |
| | | |
| | | comment on column wf_form_manage.id is '主键'; |
| | | |
| | | comment on column wf_form_manage.form_name is '表单名称'; |
| | | |
| | | comment on column wf_form_manage.form_type is '表单类型'; |
| | | |
| | | comment on column wf_form_manage.router is '路由地址/表单ID'; |
| | | |
| | | comment on column wf_form_manage.remark is '备注'; |
| | | |
| | | comment on column wf_form_manage.tenant_id is '租户id'; |
| | | |
| | | comment on column wf_form_manage.create_dept is '创建部门'; |
| | | |
| | | comment on column wf_form_manage.create_by is '创建者'; |
| | | |
| | | comment on column wf_form_manage.create_time is '创建时间'; |
| | | |
| | | comment on column wf_form_manage.update_by is '修改者'; |
| | | |
| | | comment on column wf_form_manage.update_time is '修改时间'; |
| | | |
| | | insert into wf_form_manage(id, form_name, form_type, router, remark, tenant_id, create_dept, create_by, create_time, update_by, update_time) VALUES (1, '请假申请', 'static', '/workflow/leaveEdit/index', NULL, '000000', 103, 1, now(), 1, now()); |
| | |
| | | ); |
| | | |
| | | comment on table wf_node_config is '节点配置'; |
| | | |
| | | comment on column wf_node_config.id is '主键'; |
| | | |
| | | comment on column wf_node_config.form_id is '表单id'; |
| | | |
| | | comment on column wf_node_config.form_type is '表单类型'; |
| | | |
| | | comment on column wf_node_config.node_id is '节点id'; |
| | | |
| | | comment on column wf_node_config.node_name is '节点名称'; |
| | | |
| | | comment on column wf_node_config.definition_id is '流程定义id'; |
| | | |
| | | comment on column wf_node_config.apply_user_task is '是否为申请人节点 (0是 1否)'; |
| | | |
| | | comment on column wf_node_config.tenant_id is '租户id'; |
| | | |
| | | comment on column wf_node_config.create_dept is '创建部门'; |
| | | |
| | | comment on column wf_node_config.create_by is '创建者'; |
| | | |
| | | comment on column wf_node_config.create_time is '创建时间'; |
| | | |
| | | comment on column wf_node_config.update_by is '修改者'; |
| | | |
| | | comment on column wf_node_config.update_time is '修改时间'; |
| | | |
| | | INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11638, '请假申请', 5, 1, 'leave', 'workflow/leave/index', 1, 0, 'C', '0', '0', 'workflow:leave:list', '#', 103, 1, now(), NULL, NULL, '请假申请菜单'); |