diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..8564f294 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2018 RuoYi + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..88e6d86d --- /dev/null +++ b/README.md @@ -0,0 +1,94 @@ +

+ logo +

+

RuoYi-Vue-Super

+

🎉基于最新若依前后端分离版本,同步更新,并新增功能,集成hutools,mybatis-plus,lombok,knife4j,websocket,minio文件上传,集成flowable工作流,加入可视化大屏。

+

+ + + +

+ +## 平台简介 + +若依是一套全部开源的快速开发平台,毫无保留给个人及企业免费使用。 + +* 前端采用Vue、Element UI。 +* 后端采用Spring Boot、Spring Security、Redis & Jwt。 +* 权限认证使用Jwt,支持多终端认证系统。 +* 支持加载动态权限菜单,多方式轻松权限控制。 +* 高效率开发,使用代码生成器可以一键生成前后端代码。 + +## 新增功能 +* 集成hutools,mybatis-plus,lombok,knife4j,websocket。 +* 代码生成同时适配mybatis跟mybatis-plus。 +* 集成xdh-map,基于openlayers灵活开发地图可视化,各种矢量图层,控件。 +* 在字典管理基础上增加tree树形字典管理功能,方便树形字典开发与后期动态运维。 +* 集成flowable工作流,代码引入芋道源码flowable工作流模块,适配若依官方代码,方便后期同步更新。 +* 集成可视化大屏,代码引入奔跑的面条big-screen-vue-datav大屏vue2版本。 +* [新增 mybatis-plus数据权限解决方案。](https://gitee.com/rainsuper/RuoYi-Vue-Super/wikis/pages?sort_id=5957319&doc_id=2965484) +* [新增 tenant 多租户解决方案(共享数据库方式)。](https://gitee.com/rainsuper/RuoYi-Vue-Super/wikis/pages?sort_id=5960193&doc_id=2965484) +* [新增 oss 文件上传插件,支持所有兼容s3协议的云存储:如阿里云OSS,腾讯云COS,七牛云,京东云,minio等。](https://gitee.com/rainsuper/RuoYi-Vue-Super/wikis/pages?sort_id=5966058&doc_id=2965484) + +## 参考文档 +第三方对接 +
+>[整合Oauth2.0单点方案](https://gitee.com/rainsuper/RuoYi-Vue-Super/wikis/pages?sort_id=5949355&doc_id=2965484) +## 内置功能 + +1. 用户管理:用户是系统操作者,该功能主要完成系统用户配置。 +2. 部门管理:配置系统组织机构(公司、部门、小组),树结构展现支持数据权限。 +3. 岗位管理:配置系统用户所属担任职务。 +4. 菜单管理:配置系统菜单,操作权限,按钮权限标识等。 +5. 角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。 +6. 字典管理:对系统中经常使用的一些较为固定的数据进行维护。 +7. 参数管理:对系统动态配置常用参数。 +8. 通知公告:系统通知公告信息发布维护。 +9. 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。 +10. 登录日志:系统登录日志记录查询包含登录异常。 +11. 在线用户:当前系统中活跃用户状态监控。 +12. 定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。 +13. 代码生成:前后端代码的生成(java、html、xml、sql)支持CRUD下载 。 +14. 系统接口:根据业务代码自动生成相关的api接口文档。 +15. 服务监控:监视当前系统CPU、内存、磁盘、堆栈等相关信息。 +16. 缓存监控:对系统的缓存信息查询,命令统计等。 +17. 在线构建器:拖动表单元素生成相应的HTML代码。 +18. 连接池监视:监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈。 + +## 在线体验 + +- admin/admin123 + +旧演示地址:http://vue.ruoyi.vip +旧文档地址:http://doc.ruoyi.vip + +- 新增功能按照若依文档跑下就可以看到了,或者看下边的演示图。 + +## 演示图 +- 若依官方显示图就不放了,这里只放新增的功能图片。 + + + + + + + + + + + + + + + + + + + + +
+ +## 若依Super前后端分离交流群 +QQ群: [![加入QQ群](https://img.shields.io/badge/681646796-blue.svg)](https://jq.qq.com/?_wv=1027&k=bbKX5vcb) 点击按钮入群。 + + diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..ea682b43 --- /dev/null +++ b/pom.xml @@ -0,0 +1,372 @@ + + + 4.0.0 + + com.ruoyi + ruoyi + 3.8.4 + + ruoyi + http://www.ruoyi.vip + 数字消防综合管理系统 + + + 3.8.4 + UTF-8 + UTF-8 + 1.8 + 3.1.1 + 1.2.11 + 1.21 + 3.0.0 + 2.3.2 + 2.2.2 + 1.4.6 + 2.0.16 + 6.3.0 + 2.11.0 + 1.4 + 3.2.2 + 4.1.2 + 2.3 + 0.9.1 + 5.8.5 + 3.5.2 + 1.18.12 + 3.0.3 + + 6.7.2 + 1.4.1.Final + 2.11.4 + 1.12.282 + 3.5.2 + 1.5.8 + 3.1.1 + 2.12.2 + + + + + + + xerces + xercesImpl + ${xercesImpl.version} + + + com.alibaba + easyexcel + ${easyexcel.verion} + + + + org.springframework.boot + spring-boot-dependencies + 2.5.14 + pom + import + + + + + com.alibaba + druid-spring-boot-starter + ${druid.version} + + + + + eu.bitwalker + UserAgentUtils + ${bitwalker.version} + + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + ${mybatis-spring-boot.version} + + + + + com.github.pagehelper + pagehelper-spring-boot-starter + ${pagehelper.boot.version} + + + + + com.github.oshi + oshi-core + ${oshi.version} + + + + + io.springfox + springfox-boot-starter + ${swagger.version} + + + io.swagger + swagger-models + + + + + + + commons-io + commons-io + ${commons.io.version} + + + + + commons-fileupload + commons-fileupload + ${commons.fileupload.version} + + + + + org.apache.poi + poi-ooxml + ${poi.version} + + + + + org.apache.velocity + velocity-engine-core + ${velocity.version} + + + + + commons-collections + commons-collections + ${commons.collections.version} + + + + + com.alibaba.fastjson2 + fastjson2 + ${fastjson.version} + + + + + io.jsonwebtoken + jjwt + ${jwt.version} + + + + + com.github.penggle + kaptcha + ${kaptcha.version} + + + + + com.ruoyi + ruoyi-quartz + ${ruoyi.version} + + + + + com.ruoyi + ruoyi-generator + ${ruoyi.version} + + + + + com.ruoyi + ruoyi-framework + ${ruoyi.version} + + + + + com.ruoyi + ruoyi-system + ${ruoyi.version} + + + + + com.ruoyi + ruoyi-common + ${ruoyi.version} + + + + + com.ruoyi + ruoyi-flowable + ${ruoyi.version} + + + + + cn.hutool + hutool-all + ${hutool.version} + + + + + com.baomidou + mybatis-plus-boot-starter + ${mybatis-plus.version} + + + + + org.projectlombok + lombok + true + ${lombok.version} + + + + + com.github.xiaoymin + knife4j-spring-boot-starter + ${knife4j.version} + + + mapstruct + org.mapstruct + + + + + + + org.flowable + flowable-spring-boot-starter-process + ${flowable.version} + + + org.flowable + flowable-spring-boot-starter-actuator + ${flowable.version} + + + + org.mapstruct + mapstruct + ${mapstruct.version} + + + org.mapstruct + mapstruct-jdk8 + ${mapstruct.version} + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + + + + + com.alibaba + transmittable-thread-local + ${ttl.version} + + + + com.amazonaws + aws-java-sdk-s3 + ${aws-java-sdk-s3.version} + + + + com.ruoyi + ruoyi-springboot-starter-tenant + ${ruoyi.version} + + + + + com.baomidou + mybatis-plus-generator + ${mybatis-plus-generator.version} + + + + org.jeecgframework.jimureport + jimureport-spring-boot-starter + ${jimureport.version} + + + + + + ruoyi-admin + ruoyi-framework + ruoyi-system + ruoyi-quartz + ruoyi-generator + ruoyi-common + ruoyi-flowable + ruoyi-plugin + + pom + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + + + + + + + + public + aliyun nexus + https://maven.aliyun.com/repository/public + + true + + + + + + + public + aliyun nexus + https://maven.aliyun.com/repository/public + + true + + + false + + + + + diff --git a/ruoyi-admin/Dockerfile b/ruoyi-admin/Dockerfile new file mode 100644 index 00000000..80127187 --- /dev/null +++ b/ruoyi-admin/Dockerfile @@ -0,0 +1,19 @@ +## AdoptOpenJDK 停止发布 OpenJDK 二进制,而 Eclipse Temurin 是它的延伸,提供更好的稳定性 +FROM openjdk:8-jdk-alpine +#FROM eclipse-temurin:8-jre + +## 创建目录,并使用它作为工作目录 +#RUN mkdir -p /project/hzyh-admin +#WORKDIR /project/hzyh-admin +## 将后端项目的 Jar 文件,复制到镜像中 +COPY target/ruoyi-admin.jar hzyh-admin.jar + +## 设置 TZ 时区 +## 设置 JAVA_OPTS 环境变量,可通过 docker run -e "JAVA_OPTS=" 进行覆盖 +ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms512m -Xmx1024m" + +## 暴露后端项目的 48080 端口 +EXPOSE 8080 + +## 启动后端项目 +CMD java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -Dspring.profiles.active=${HJ} -jar hzyh-admin.jar diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml new file mode 100644 index 00000000..0b8c2a0d --- /dev/null +++ b/ruoyi-admin/pom.xml @@ -0,0 +1,120 @@ + + + + ruoyi + com.ruoyi + 3.8.4 + + 4.0.0 + jar + ruoyi-admin + + + web服务入口 + + + + + + + + + + + org.jsoup + jsoup + 1.14.3 + + + + + + + + + + + mysql + mysql-connector-java + + + + + com.ruoyi + ruoyi-framework + + + + + com.ruoyi + ruoyi-quartz + + + + + com.ruoyi + ruoyi-generator + + + + + com.github.xiaoymin + knife4j-spring-boot-starter + + + + + com.ruoyi + ruoyi-flowable + + + mybatis + org.mybatis + + + + + org.jeecgframework.jimureport + jimureport-spring-boot-starter + + + jsqlparser + com.github.jsqlparser + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + 2.1.1.RELEASE + + true + + + + + repackage + + + + + + org.apache.maven.plugins + maven-war-plugin + 3.1.0 + + false + ${project.artifactId} + + + + ${project.artifactId} + + + diff --git a/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java b/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java new file mode 100644 index 00000000..04873d70 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java @@ -0,0 +1,32 @@ +package com.ruoyi; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.scheduling.annotation.EnableScheduling; + +/** + * 启动程序 + * + * @author ruoyi + */ +@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class },scanBasePackages = {"org.jeecg.modules.jmreport","com.ruoyi"}) +@EnableScheduling +public class RuoYiApplication +{ + public static void main(String[] args) + { + // System.setProperty("spring.devtools.restart.enabled", "false"); + SpringApplication.run(RuoYiApplication.class, args); + System.out.println("(♥◠‿◠)ノ゙ 泽火启动成功 ლ(´ڡ`ლ)゙ \n" + + " .-------. ____ __ \n" + + " | _ _ \\ \\ \\ / / \n" + + " | ( ' ) | \\ _. / ' \n" + + " |(_ o _) / _( )_ .' \n" + + " | (_,_).' __ ___(_ o _)' \n" + + " | |\\ \\ | || |(_,_)' \n" + + " | | \\ `' /| `-' / \n" + + " | | \\ / \\ / \n" + + " ''-' `'-' `-..-' "); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/RuoYiServletInitializer.java b/ruoyi-admin/src/main/java/com/ruoyi/RuoYiServletInitializer.java new file mode 100644 index 00000000..6de67dc7 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/RuoYiServletInitializer.java @@ -0,0 +1,18 @@ +package com.ruoyi; + +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +/** + * web容器中进行部署 + * + * @author ruoyi + */ +public class RuoYiServletInitializer extends SpringBootServletInitializer +{ + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) + { + return application.sources(RuoYiApplication.class); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/TestFlowableApplication.java b/ruoyi-admin/src/main/java/com/ruoyi/TestFlowableApplication.java new file mode 100644 index 00000000..c028ae2d --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/TestFlowableApplication.java @@ -0,0 +1,17 @@ +package com.ruoyi; + +import org.flowable.engine.ProcessEngine; +import org.flowable.engine.ProcessEngineConfiguration; +import org.flowable.engine.impl.cfg.StandaloneProcessEngineConfiguration; + +public class TestFlowableApplication { + public static void main(String[] args) { + ProcessEngineConfiguration cfg = new StandaloneProcessEngineConfiguration() + .setJdbcUrl("jdbc:mysql://60.204.223.58:3306/flowable?serverTimezone=UTC&nullCatalogMeansCurrent=true") + .setJdbcUsername("root") + .setJdbcPassword("Mysql123!") + .setJdbcDriver("com.mysql.cj.jdbc.Driver") + .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE); + ProcessEngine processEngine = cfg.buildProcessEngine(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/DevinUtil.java b/ruoyi-admin/src/main/java/com/ruoyi/web/DevinUtil.java new file mode 100644 index 00000000..77264b63 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/DevinUtil.java @@ -0,0 +1,85 @@ +package com.ruoyi.web; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.StrUtil; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * + */ +public class DevinUtil { + public static void main(String[] args) { + copyFileFromModule(); + } + + /** + * 基于某个菜单功能进行快速拷贝生成后端和web代码 + */ + private static void copyFileFromModule() { + Map replaceMap = new HashMap<>(); //要替换掉的内容。注意大小写 + //基于标识管理功能,快速拷贝相关文件 + String newEntityName = "FireStationTrainRecord"; + String oldEntityName = "IdentityManage"; + replaceMap.put("bs_identity_manage", "bs_firestation_trainrecord"); + replaceMap.put("identityManage", "fireStationTrainRecord"); + replaceMap.put("标识管理", "训练记录"); + replaceMap.put(oldEntityName, newEntityName); //实体名 + replaceMap.put(oldEntityName.toLowerCase(), newEntityName.toLowerCase()); //全小写的替换 + List replaceFile = new ArrayList<>(); + //后端代码类 + replaceFile.add("D:\\project\\itcqp\\RuoYi-Vue-Super\\ruoyi-system\\src\\main\\java\\com\\ruoyi\\system\\domain\\Bs" + oldEntityName + ".java"); + replaceFile.add("D:\\project\\itcqp\\RuoYi-Vue-Super\\ruoyi-admin\\src\\main\\java\\com\\ruoyi\\web\\controller\\system\\Bs" + oldEntityName + "Controller.java"); + replaceFile.add("D:\\project\\itcqp\\RuoYi-Vue-Super\\ruoyi-system\\src\\main\\java\\com\\ruoyi\\system\\mapper\\Bs" + oldEntityName + "Mapper.java"); + replaceFile.add("D:\\project\\itcqp\\RuoYi-Vue-Super\\ruoyi-system\\src\\main\\resources\\mapper\\system\\Bs" + oldEntityName + "Mapper.xml"); + replaceFile.add("D:\\project\\itcqp\\RuoYi-Vue-Super\\ruoyi-system\\src\\main\\java\\com\\ruoyi\\system\\service\\impl\\Bs" + oldEntityName + "ServiceImpl.java"); + replaceFile.add("D:\\project\\itcqp\\RuoYi-Vue-Super\\ruoyi-system\\src\\main\\java\\com\\ruoyi\\system\\service\\IBs" + oldEntityName + "Service.java"); + //前端代码类 + //前端vue文件夹路径 + replaceFile.add("D:\\project\\itcqp\\RuoYi-APP\\ruoyi-ui\\src\\views\\system\\"+oldEntityName.toLowerCase()); + //js 路径 + replaceFile.add("D:\\project\\itcqp\\RuoYi-APP\\ruoyi-ui\\src\\api\\system\\"+oldEntityName.toLowerCase()+".js"); + for (String filepath : replaceFile) { + File file = FileUtil.file(filepath); + if (!file.exists()) { + System.out.println("文件不存在:" + filepath); + continue; + } + if (file.isDirectory()) { + //前端目录 + FileUtil.walkFiles(FileUtil.file(filepath),tf->{ + writeNewFile(replaceMap, newEntityName, oldEntityName, tf,true); + }); + }else{ + //文件 + writeNewFile(replaceMap, newEntityName, oldEntityName, file,true); + } + } + } + + private static void writeNewFile(Map replaceMap, String entityName, String oldEntityName, File file, boolean needReplaceFileName) { + String newfilepath = file.getAbsolutePath(); + if (needReplaceFileName) { + newfilepath = file.getAbsolutePath().replace(oldEntityName, entityName).replace(oldEntityName.toLowerCase(),entityName.toLowerCase()); //第二个是为了js文件 + } + List lines = FileUtil.readLines(file.getAbsolutePath(), "utf-8"); + List newlines = new ArrayList<>(); + for (String line : lines) { + String newLine = line; + if (StrUtil.isNotBlank(line)) { + for (String key : replaceMap.keySet()) { + newLine = newLine.replace(key, replaceMap.get(key)); + } + } + newlines.add(newLine); + } + if (!FileUtil.exist(newfilepath)) { + //不存在才创建 + FileUtil.writeLines(newlines,newfilepath,"utf-8"); + } + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java new file mode 100644 index 00000000..bec40373 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java @@ -0,0 +1,92 @@ +package com.ruoyi.web.controller.common; + +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import javax.annotation.Resource; +import javax.imageio.ImageIO; +import javax.servlet.http.HttpServletResponse; + +import com.ruoyi.common.utils.EhcacheUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.FastByteArrayOutputStream; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import com.google.code.kaptcha.Producer; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.sign.Base64; +import com.ruoyi.common.utils.uuid.IdUtils; +import com.ruoyi.system.service.ISysConfigService; + +/** + * 验证码操作处理 + * + * @author ruoyi + */ +@RestController +public class CaptchaController +{ + @Resource(name = "captchaProducer") + private Producer captchaProducer; + + @Resource(name = "captchaProducerMath") + private Producer captchaProducerMath; + + @Autowired + private ISysConfigService configService; + /** + * 生成验证码 + */ + @GetMapping("/captchaImage") + public AjaxResult getCode(HttpServletResponse response) throws IOException + { + AjaxResult ajax = AjaxResult.success(); + boolean captchaEnabled = configService.selectCaptchaEnabled(); + ajax.put("captchaEnabled", captchaEnabled); + if (!captchaEnabled) + { + return ajax; + } + + // 保存验证码信息 + String uuid = IdUtils.simpleUUID(); + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid; + + String capStr = null, code = null; + BufferedImage image = null; + + // 生成验证码 + String captchaType = RuoYiConfig.getCaptchaType(); + if ("math".equals(captchaType)) + { + String capText = captchaProducerMath.createText(); + capStr = capText.substring(0, capText.lastIndexOf("@")); + code = capText.substring(capText.lastIndexOf("@") + 1); + image = captchaProducerMath.createImage(capStr); + } + else if ("char".equals(captchaType)) + { + capStr = code = captchaProducer.createText(); + image = captchaProducer.createImage(capStr); + } + EhcacheUtil.put("verify",verifyKey,code); +// redisCache.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES); + // 转换流信息写出 + FastByteArrayOutputStream os = new FastByteArrayOutputStream(); + try + { + ImageIO.write(image, "jpg", os); + } + catch (IOException e) + { + return AjaxResult.error(e.getMessage()); + } + + ajax.put("uuid", uuid); + ajax.put("img", Base64.encode(os.toByteArray())); + return ajax; + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java new file mode 100644 index 00000000..62ea52e4 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java @@ -0,0 +1,163 @@ +package com.ruoyi.web.controller.common; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONObject; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.file.FileUploadUtils; +import com.ruoyi.common.utils.file.FileUtils; +import com.ruoyi.framework.config.ServerConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.ArrayList; +import java.util.List; + +/** + * 通用请求处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/common") +public class CommonController { + private static final Logger log = LoggerFactory.getLogger(CommonController.class); + + @Autowired + private ServerConfig serverConfig; + + private static final String FILE_DELIMETER = ","; + + /** + * 通用下载请求 + * + * @param fileName 文件名称 + * @param delete 是否删除 + */ + @GetMapping("/download") + public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request) { + try { + if (!FileUtils.checkAllowDownload(fileName)) { + throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName)); + } + String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1); + String filePath = RuoYiConfig.getDownloadPath() + fileName; + + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + FileUtils.setAttachmentResponseHeader(response, realFileName); + FileUtils.writeBytes(filePath, response.getOutputStream()); + if (delete) { + FileUtils.deleteFile(filePath); + } + } catch (Exception e) { + log.error("下载文件失败", e); + } + } + + /** + * 通用上传请求(单个) + */ + @PostMapping("/upload") + public AjaxResult uploadFile(MultipartFile file) throws Exception { + try { + // 上传文件路径 + String filePath = RuoYiConfig.getUploadPath(); + // 上传并返回新文件名称 + String fileName = FileUploadUtils.upload(filePath, file); + String url = serverConfig.getUrl() + fileName; + AjaxResult ajax = AjaxResult.success(); + ajax.put("url", url); + ajax.put("fileName", fileName); + ajax.put("newFileName", FileUtils.getName(fileName)); + ajax.put("originalFilename", file.getOriginalFilename()); + return ajax; + } catch (Exception e) { + return AjaxResult.error(e.getMessage()); + } + } + + /** + * 通用上传请求(多个) + */ + @PostMapping("/uploads") + public AjaxResult uploadFiles(List files) throws Exception { + try { + // 上传文件路径 + String filePath = RuoYiConfig.getUploadPath(); + List urls = new ArrayList(); + List fileNames = new ArrayList(); + List newFileNames = new ArrayList(); + List originalFilenames = new ArrayList(); + for (MultipartFile file : files) { + // 上传并返回新文件名称 + String fileName = FileUploadUtils.upload(filePath, file); + String url = serverConfig.getUrl() + fileName; + urls.add(url); + fileNames.add(fileName); + newFileNames.add(FileUtils.getName(fileName)); + originalFilenames.add(file.getOriginalFilename()); + } + AjaxResult ajax = AjaxResult.success(); + ajax.put("urls", StringUtils.join(urls, FILE_DELIMETER)); + ajax.put("fileNames", StringUtils.join(fileNames, FILE_DELIMETER)); + ajax.put("newFileNames", StringUtils.join(newFileNames, FILE_DELIMETER)); + ajax.put("originalFilenames", StringUtils.join(originalFilenames, FILE_DELIMETER)); + return ajax; + } catch (Exception e) { + return AjaxResult.error(e.getMessage()); + } + } + + /** + * 本地资源通用下载 + */ + @GetMapping("/download/resource") + public void resourceDownload(String resource, HttpServletRequest request, HttpServletResponse response) + throws Exception { + try { + if (!FileUtils.checkAllowDownload(resource)) { + throw new Exception(StringUtils.format("资源文件({})非法,不允许下载。 ", resource)); + } + // 本地资源路径 + String localPath = RuoYiConfig.getProfile(); + // 数据库资源地址 + String downloadPath = localPath + StringUtils.substringAfter(resource, Constants.RESOURCE_PREFIX); + // 下载名称 + String downloadName = StringUtils.substringAfterLast(downloadPath, "/"); + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + FileUtils.setAttachmentResponseHeader(response, downloadName); + FileUtils.writeBytes(downloadPath, response.getOutputStream()); + } catch (Exception e) { + log.error("下载文件失败", e); + } + } + + /** + * 本地资源通用下载 + */ + @PostMapping("/chatgpt") + public Object chatgpt(@RequestBody JSONObject param) throws Exception { + HttpRequest post = HttpUtil.createPost("https://api.aioschat.com/"); + post.body(param.toString()); + String body = post.execute().body(); + if (StrUtil.isNotBlank(body) && body.contains("请稍后再试")) { + System.out.println("第2次"); + body = post.execute().body(); + if (StrUtil.isNotBlank(body) && body.contains("请稍后再试")) { + System.out.println("第三次"); + body = post.execute().body(); + } + } + return body; + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java new file mode 100644 index 00000000..9aaf45bf --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java @@ -0,0 +1,120 @@ +//package com.ruoyi.web.controller.monitor; +// +//import java.util.ArrayList; +//import java.util.Collection; +//import java.util.HashMap; +//import java.util.List; +//import java.util.Map; +//import java.util.Properties; +//import java.util.Set; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.data.redis.core.RedisCallback; +//import org.springframework.data.redis.core.RedisTemplate; +//import org.springframework.security.access.prepost.PreAuthorize; +//import org.springframework.web.bind.annotation.DeleteMapping; +//import org.springframework.web.bind.annotation.GetMapping; +//import org.springframework.web.bind.annotation.PathVariable; +//import org.springframework.web.bind.annotation.RequestMapping; +//import org.springframework.web.bind.annotation.RestController; +//import com.ruoyi.common.constant.CacheConstants; +//import com.ruoyi.common.core.domain.AjaxResult; +//import com.ruoyi.common.utils.StringUtils; +//import com.ruoyi.system.domain.SysCache; +// +///** +// * 缓存监控 +// * +// * @author ruoyi +// */ +//@RestController +//@RequestMapping("/monitor/cache") +//public class CacheController +//{ +// @Autowired +// private RedisTemplate redisTemplate; +// +// private final static List caches = new ArrayList(); +// { +// caches.add(new SysCache(CacheConstants.LOGIN_TOKEN_KEY, "用户信息")); +// caches.add(new SysCache(CacheConstants.SYS_CONFIG_KEY, "配置信息")); +// caches.add(new SysCache(CacheConstants.SYS_DICT_KEY, "数据字典")); +// 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(CacheConstants.PWD_ERR_CNT_KEY, "密码错误次数")); +// } +// +// @PreAuthorize("@ss.hasPermi('monitor:cache:list')") +// @GetMapping() +// public AjaxResult getInfo() throws Exception +// { +// Properties info = (Properties) redisTemplate.execute((RedisCallback) connection -> connection.info()); +// Properties commandStats = (Properties) redisTemplate.execute((RedisCallback) connection -> connection.info("commandstats")); +// Object dbSize = redisTemplate.execute((RedisCallback) connection -> connection.dbSize()); +// +// Map result = new HashMap<>(3); +// result.put("info", info); +// result.put("dbSize", dbSize); +// +// List> pieList = new ArrayList<>(); +// commandStats.stringPropertyNames().forEach(key -> { +// Map 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); +// }); +// result.put("commandStats", pieList); +// return AjaxResult.success(result); +// } +// +// @PreAuthorize("@ss.hasPermi('monitor:cache:list')") +// @GetMapping("/getNames") +// public AjaxResult cache() +// { +// return AjaxResult.success(caches); +// } +// +// @PreAuthorize("@ss.hasPermi('monitor:cache:list')") +// @GetMapping("/getKeys/{cacheName}") +// public AjaxResult getCacheKeys(@PathVariable String cacheName) +// { +// Set cacheKeys = redisTemplate.keys(cacheName + "*"); +// return AjaxResult.success(cacheKeys); +// } +// +// @PreAuthorize("@ss.hasPermi('monitor:cache:list')") +// @GetMapping("/getValue/{cacheName}/{cacheKey}") +// public AjaxResult getCacheValue(@PathVariable String cacheName, @PathVariable String cacheKey) +// { +// String cacheValue = redisTemplate.opsForValue().get(cacheKey); +// SysCache sysCache = new SysCache(cacheName, cacheKey, cacheValue); +// return AjaxResult.success(sysCache); +// } +// +// @PreAuthorize("@ss.hasPermi('monitor:cache:list')") +// @DeleteMapping("/clearCacheName/{cacheName}") +// public AjaxResult clearCacheName(@PathVariable String cacheName) +// { +// Collection cacheKeys = redisTemplate.keys(cacheName + "*"); +// redisTemplate.delete(cacheKeys); +// return AjaxResult.success(); +// } +// +// @PreAuthorize("@ss.hasPermi('monitor:cache:list')") +// @DeleteMapping("/clearCacheKey/{cacheKey}") +// public AjaxResult clearCacheKey(@PathVariable String cacheKey) +// { +// redisTemplate.delete(cacheKey); +// return AjaxResult.success(); +// } +// +// @PreAuthorize("@ss.hasPermi('monitor:cache:list')") +// @DeleteMapping("/clearCacheAll") +// public AjaxResult clearCacheAll() +// { +// Collection cacheKeys = redisTemplate.keys("*"); +// redisTemplate.delete(cacheKeys); +// return AjaxResult.success(); +// } +//} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java new file mode 100644 index 00000000..89ee27d0 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java @@ -0,0 +1,25 @@ +package com.ruoyi.web.controller.monitor; + +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.framework.web.domain.Server; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 服务器监控 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/server") +public class ServerController { + @PreAuthorize("@ss.hasPermi('monitor:server:list')") + @GetMapping() + public AjaxResult getInfo() throws Exception { + Server server = new Server(); + server.copyTo(); + return AjaxResult.success(server); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java new file mode 100644 index 00000000..e1112377 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java @@ -0,0 +1,83 @@ +package com.ruoyi.web.controller.monitor; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; + +import com.ruoyi.framework.web.service.SysPasswordService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.system.domain.SysLogininfor; +import com.ruoyi.system.service.ISysLogininforService; + +/** + * 系统访问记录 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/logininfor") +public class SysLogininforController extends BaseController +{ + @Autowired + private ISysLogininforService logininforService; + + @Autowired + private SysPasswordService passwordService; + + @PreAuthorize("@ss.hasPermi('monitor:logininfor:list')") + @GetMapping("/list") + public TableDataInfo list(SysLogininfor logininfor) + { + startPage(); + List list = logininforService.selectLogininforList(logininfor); + return getDataTable(list); + } + + @Log(title = "登录日志", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('monitor:logininfor:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysLogininfor logininfor) + { + List list = logininforService.selectLogininforList(logininfor); + ExcelUtil util = new ExcelUtil(SysLogininfor.class); + util.exportExcel(response, list, "登录日志"); + } + + @PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')") + @Log(title = "登录日志", businessType = BusinessType.DELETE) + @DeleteMapping("/{infoIds}") + public AjaxResult remove(@PathVariable Long[] infoIds) + { + return toAjax(logininforService.deleteLogininforByIds(infoIds)); + } + + @PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')") + @Log(title = "登录日志", businessType = BusinessType.CLEAN) + @DeleteMapping("/clean") + public AjaxResult clean() + { + logininforService.cleanLogininfor(); + return success(); + } + + @PreAuthorize("@ss.hasPermi('monitor:logininfor:unlock')") + @Log(title = "账户解锁", businessType = BusinessType.OTHER) + @GetMapping("/unlock/{userName}") + public AjaxResult unlock(@PathVariable("userName") String userName) + { + passwordService.clearLoginRecordCache(userName); + return success(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java new file mode 100644 index 00000000..4656451f --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java @@ -0,0 +1,60 @@ +package com.ruoyi.web.controller.monitor; + +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.system.domain.SysOperLog; +import com.ruoyi.system.service.ISysOperLogService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 操作日志记录 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/operlog") +public class SysOperlogController extends BaseController { + @Autowired + private ISysOperLogService operLogService; + + @PreAuthorize("@ss.hasPermi('monitor:operlog:list')") + @GetMapping("/list") + public TableDataInfo list(SysOperLog operLog) { + startPage(); + List list = operLogService.selectOperLogList(operLog); + return getDataTable(list); + } + + @Log(title = "操作日志", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('monitor:operlog:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysOperLog operLog) { + List list = operLogService.selectOperLogList(operLog); + ExcelUtil util = new ExcelUtil(SysOperLog.class); + util.exportExcel(response, list, "操作日志"); + } + + @Log(title = "操作日志", businessType = BusinessType.DELETE) + @PreAuthorize("@ss.hasPermi('monitor:operlog:remove')") + @DeleteMapping("/{operIds}") + public AjaxResult remove(@PathVariable Long[] operIds) { + return toAjax(operLogService.deleteOperLogByIds(operIds)); + } + + @Log(title = "操作日志", businessType = BusinessType.CLEAN) + @PreAuthorize("@ss.hasPermi('monitor:operlog:remove')") + @DeleteMapping("/clean") + public AjaxResult clean() { + operLogService.cleanOperLog(); + return success(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java new file mode 100644 index 00000000..45ecb03a --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java @@ -0,0 +1,92 @@ +package com.ruoyi.web.controller.monitor; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import com.ruoyi.common.utils.EhcacheUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.domain.SysUserOnline; +import com.ruoyi.system.service.ISysUserOnlineService; + +/** + * 在线用户监控 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/online") +public class SysUserOnlineController extends BaseController +{ + @Autowired + private ISysUserOnlineService userOnlineService; + + + @PreAuthorize("@ss.hasPermi('monitor:online:list')") + @GetMapping("/list") + public TableDataInfo list(String ipaddr, String userName) + { +//// Collection keys = redisCache.keys(CacheConstants.LOGIN_TOKEN_KEY + "*"); +// List userOnlineList = new ArrayList(); +// for (String key : keys) +// { +// LoginUser user = redisCache.getCacheObject(key); +// if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName)) +// { +// if (StringUtils.equals(ipaddr, user.getIpaddr()) && StringUtils.equals(userName, user.getUsername())) +// { +// userOnlineList.add(userOnlineService.selectOnlineByInfo(ipaddr, userName, user)); +// } +// } +// else if (StringUtils.isNotEmpty(ipaddr)) +// { +// if (StringUtils.equals(ipaddr, user.getIpaddr())) +// { +// userOnlineList.add(userOnlineService.selectOnlineByIpaddr(ipaddr, user)); +// } +// } +// else if (StringUtils.isNotEmpty(userName) && StringUtils.isNotNull(user.getUser())) +// { +// if (StringUtils.equals(userName, user.getUsername())) +// { +// userOnlineList.add(userOnlineService.selectOnlineByUserName(userName, user)); +// } +// } +// else +// { +// userOnlineList.add(userOnlineService.loginUserToUserOnline(user)); +// } +// } +// Collections.reverse(userOnlineList); +// userOnlineList.removeAll(Collections.singleton(null)); +// return getDataTable(userOnlineList); + return null; + } + + /** + * 强退用户 + */ + @PreAuthorize("@ss.hasPermi('monitor:online:forceLogout')") + @Log(title = "在线用户", businessType = BusinessType.FORCE) + @DeleteMapping("/{tokenId}") + public AjaxResult forceLogout(@PathVariable String tokenId) + { +// redisCache.deleteObject(CacheConstants.LOGIN_TOKEN_KEY + tokenId); + return AjaxResult.success(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java new file mode 100644 index 00000000..922687c3 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java @@ -0,0 +1,117 @@ +package com.ruoyi.web.controller.system; + +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.system.domain.SysConfig; +import com.ruoyi.system.service.ISysConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 参数配置 信息操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/config") +public class SysConfigController extends BaseController { + @Autowired + private ISysConfigService configService; + + /** + * 获取参数配置列表 + */ + @PreAuthorize("@ss.hasPermi('system:config:list')") + @GetMapping("/list") + public TableDataInfo list(SysConfig config) { + startPage(); + List list = configService.selectConfigList(config); + return getDataTable(list); + } + + @Log(title = "参数管理", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:config:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysConfig config) { + List list = configService.selectConfigList(config); + ExcelUtil util = new ExcelUtil(SysConfig.class); + util.exportExcel(response, list, "参数数据"); + } + + /** + * 根据参数编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:config:query')") + @GetMapping(value = "/{configId}") + public AjaxResult getInfo(@PathVariable Long configId) { + return success(configService.selectConfigById(configId)); + } + + /** + * 根据参数键名查询参数值 + */ + @GetMapping(value = "/configKey/{configKey}") + public AjaxResult getConfigKey(@PathVariable String configKey) { + return success(configService.selectConfigByKey(configKey)); + } + + /** + * 新增参数配置 + */ + @PreAuthorize("@ss.hasPermi('system:config:add')") + @Log(title = "参数管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysConfig config) { + if (UserConstants.NOT_UNIQUE.equals(configService.checkConfigKeyUnique(config))) { + return error("新增参数'" + config.getConfigName() + "'失败,参数键名已存在"); + } + config.setCreateBy(getUsername()); + return toAjax(configService.insertConfig(config)); + } + + /** + * 修改参数配置 + */ + @PreAuthorize("@ss.hasPermi('system:config:edit')") + @Log(title = "参数管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysConfig config) { + if (UserConstants.NOT_UNIQUE.equals(configService.checkConfigKeyUnique(config))) { + return error("修改参数'" + config.getConfigName() + "'失败,参数键名已存在"); + } + config.setUpdateBy(getUsername()); + return toAjax(configService.updateConfig(config)); + } + + /** + * 删除参数配置 + */ + @PreAuthorize("@ss.hasPermi('system:config:remove')") + @Log(title = "参数管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{configIds}") + public AjaxResult remove(@PathVariable Long[] configIds) { + configService.deleteConfigByIds(configIds); + return success(); + } + + /** + * 刷新参数缓存 + */ + @PreAuthorize("@ss.hasPermi('system:config:remove')") + @Log(title = "参数管理", businessType = BusinessType.CLEAN) + @DeleteMapping("/refreshCache") + public AjaxResult refreshCache() { + configService.resetConfigCache(); + return success(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java new file mode 100644 index 00000000..79a73787 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java @@ -0,0 +1,125 @@ +package com.ruoyi.web.controller.system; + +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysDept; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.service.ISysDeptService; +import org.apache.commons.lang3.ArrayUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.Comparator; +import java.util.List; + +/** + * 部门信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/dept") +public class SysDeptController extends BaseController { + @Autowired + private ISysDeptService deptService; + + /** + * 获取部门列表 + */ + @PreAuthorize("@ss.hasPermi('system:dept:list')") + @GetMapping("/list") + public AjaxResult list(SysDept dept) { + List depts = deptService.selectDeptList(dept); + return success(depts); + } + + /** + * 查询部门列表(排除节点) + */ + @PreAuthorize("@ss.hasPermi('system:dept:list')") + @GetMapping("/list/exclude/{deptId}") + public AjaxResult excludeChild(@PathVariable(value = "deptId", required = false) Long deptId) { + List depts = deptService.selectDeptList(new SysDept()); + depts.removeIf(d -> d.getDeptId().intValue() == deptId || ArrayUtils.contains(StringUtils.split(d.getAncestors(), ","), deptId + "")); + return success(depts); + } + + /** + * 根据部门编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:dept:query')") + @GetMapping(value = "/{deptId}") + public AjaxResult getInfo(@PathVariable Long deptId) { + deptService.checkDeptDataScope(deptId); + return success(deptService.selectDeptById(deptId)); + } + + /** + * 新增部门 + */ + @PreAuthorize("@ss.hasPermi('system:dept:add')") + @Log(title = "部门管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysDept dept) { + if (UserConstants.NOT_UNIQUE.equals(deptService.checkDeptNameUnique(dept))) { + return error("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在"); + } + dept.setCreateBy(getUsername()); + return toAjax(deptService.insertDept(dept)); + } + + /** + * 修改部门 + */ + @PreAuthorize("@ss.hasPermi('system:dept:edit')") + @Log(title = "部门管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysDept dept) { + Long deptId = dept.getDeptId(); + deptService.checkDeptDataScope(deptId); + if (UserConstants.NOT_UNIQUE.equals(deptService.checkDeptNameUnique(dept))) { + return error("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在"); + } else if (dept.getParentId().equals(deptId)) { + return error("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己"); + } else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus()) && deptService.selectNormalChildrenDeptById(deptId) > 0) { + return error("该部门包含未停用的子部门!"); + } + dept.setUpdateBy(getUsername()); + return toAjax(deptService.updateDept(dept)); + } + + /** + * 删除部门 + */ + @PreAuthorize("@ss.hasPermi('system:dept:remove')") + @Log(title = "部门管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{deptId}") + public AjaxResult remove(@PathVariable Long deptId) { + if (deptService.hasChildByDeptId(deptId)) { + return warn("存在下级部门,不允许删除"); + } + if (deptService.checkDeptExistUser(deptId)) { + return warn("部门存在用户,不允许删除"); + } + deptService.checkDeptDataScope(deptId); + return toAjax(deptService.deleteDeptById(deptId)); + } + + /** + * 获取部门精简信息列表 + * 只包含被开启的部门,主要用于前端的下拉选项 + */ + @GetMapping("/list-all-simple") + public AjaxResult getSimpleDepts() { + List list = deptService.selectDeptList(new SysDept()); + // 排序后,返回给前端 + list.sort(Comparator.comparing(SysDept::getOrderNum)); + return success(list); + } + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java new file mode 100644 index 00000000..8f4d4843 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java @@ -0,0 +1,106 @@ +package com.ruoyi.web.controller.system; + +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysDictData; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.system.service.ISysDictDataService; +import com.ruoyi.system.service.ISysDictTypeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletResponse; +import java.util.ArrayList; +import java.util.List; + +/** + * 数据字典信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/dict/data") +public class SysDictDataController extends BaseController { + @Autowired + private ISysDictDataService dictDataService; + + @Autowired + private ISysDictTypeService dictTypeService; + + @PreAuthorize("@ss.hasPermi('system:dict:list')") + @GetMapping("/list") + public TableDataInfo list(SysDictData dictData) { + startPage(); + List list = dictDataService.selectDictDataList(dictData); + return getDataTable(list); + } + + @Log(title = "字典数据", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:dict:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysDictData dictData) { + List list = dictDataService.selectDictDataList(dictData); + ExcelUtil util = new ExcelUtil(SysDictData.class); + util.exportExcel(response, list, "字典数据"); + } + + /** + * 查询字典数据详细 + */ + @PreAuthorize("@ss.hasPermi('system:dict:query')") + @GetMapping(value = "/{dictCode}") + public AjaxResult getInfo(@PathVariable Long dictCode) { + return success(dictDataService.selectDictDataById(dictCode)); + } + + /** + * 根据字典类型查询字典数据信息 + */ + @GetMapping(value = "/type/{dictType}") + public AjaxResult dictType(@PathVariable String dictType) { + List data = dictTypeService.selectDictDataByType(dictType); + if (StringUtils.isNull(data)) { + data = new ArrayList(); + } + return success(data); + } + + /** + * 新增字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:add')") + @Log(title = "字典数据", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysDictData dict) { + dict.setCreateBy(getUsername()); + return toAjax(dictDataService.insertDictData(dict)); + } + + /** + * 修改保存字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:edit')") + @Log(title = "字典数据", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysDictData dict) { + dict.setUpdateBy(getUsername()); + return toAjax(dictDataService.updateDictData(dict)); + } + + /** + * 删除字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:remove')") + @Log(title = "字典类型", businessType = BusinessType.DELETE) + @DeleteMapping("/{dictCodes}") + public AjaxResult remove(@PathVariable Long[] dictCodes) { + dictDataService.deleteDictDataByIds(dictCodes); + return success(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java new file mode 100644 index 00000000..7ac33a21 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java @@ -0,0 +1,115 @@ +package com.ruoyi.web.controller.system; + +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysDictType; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.system.service.ISysDictTypeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 数据字典信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/dict/type") +public class SysDictTypeController extends BaseController { + @Autowired + private ISysDictTypeService dictTypeService; + + @PreAuthorize("@ss.hasPermi('system:dict:list')") + @GetMapping("/list") + public TableDataInfo list(SysDictType dictType) { + startPage(); + List list = dictTypeService.selectDictTypeList(dictType); + return getDataTable(list); + } + + @Log(title = "字典类型", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:dict:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysDictType dictType) { + List list = dictTypeService.selectDictTypeList(dictType); + ExcelUtil util = new ExcelUtil(SysDictType.class); + util.exportExcel(response, list, "字典类型"); + } + + /** + * 查询字典类型详细 + */ + @PreAuthorize("@ss.hasPermi('system:dict:query')") + @GetMapping(value = "/{dictId}") + public AjaxResult getInfo(@PathVariable Long dictId) { + return success(dictTypeService.selectDictTypeById(dictId)); + } + + /** + * 新增字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:add')") + @Log(title = "字典类型", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysDictType dict) { + if (UserConstants.NOT_UNIQUE.equals(dictTypeService.checkDictTypeUnique(dict))) { + return error("新增字典'" + dict.getDictName() + "'失败,字典类型已存在"); + } + dict.setCreateBy(getUsername()); + return toAjax(dictTypeService.insertDictType(dict)); + } + + /** + * 修改字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:edit')") + @Log(title = "字典类型", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysDictType dict) { + if (UserConstants.NOT_UNIQUE.equals(dictTypeService.checkDictTypeUnique(dict))) { + return error("修改字典'" + dict.getDictName() + "'失败,字典类型已存在"); + } + dict.setUpdateBy(getUsername()); + return toAjax(dictTypeService.updateDictType(dict)); + } + + /** + * 删除字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:remove')") + @Log(title = "字典类型", businessType = BusinessType.DELETE) + @DeleteMapping("/{dictIds}") + public AjaxResult remove(@PathVariable Long[] dictIds) { + dictTypeService.deleteDictTypeByIds(dictIds); + return success(); + } + + /** + * 刷新字典缓存 + */ + @PreAuthorize("@ss.hasPermi('system:dict:remove')") + @Log(title = "字典类型", businessType = BusinessType.CLEAN) + @DeleteMapping("/refreshCache") + public AjaxResult refreshCache() { + dictTypeService.resetDictCache(); + return success(); + } + + /** + * 获取字典选择框列表 + */ + @GetMapping("/optionselect") + public AjaxResult optionselect() { + List dictTypes = dictTypeService.selectDictTypeAll(); + return success(dictTypes); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java new file mode 100644 index 00000000..13007eb1 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java @@ -0,0 +1,29 @@ +package com.ruoyi.web.controller.system; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.utils.StringUtils; + +/** + * 首页 + * + * @author ruoyi + */ +@RestController +public class SysIndexController +{ + /** 系统基础配置 */ + @Autowired + private RuoYiConfig ruoyiConfig; + + /** + * 访问首页,提示语 + */ + @RequestMapping("/") + public String index() + { + return StringUtils.format("欢迎使用{}后台管理框架,当前版本:v{},请通过前端地址访问。", ruoyiConfig.getName(), ruoyiConfig.getVersion()); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java new file mode 100644 index 00000000..f2e59cc8 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java @@ -0,0 +1,83 @@ +package com.ruoyi.web.controller.system; + +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysMenu; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginBody; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.framework.web.service.SysLoginService; +import com.ruoyi.framework.web.service.SysPermissionService; +import com.ruoyi.system.service.ISysMenuService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.Set; + +/** + * 登录验证 + * + * @author ruoyi + */ +@RestController +public class SysLoginController { + @Autowired + private SysLoginService loginService; + + @Autowired + private ISysMenuService menuService; + + @Autowired + private SysPermissionService permissionService; + + /** + * 登录方法 + * + * @param loginBody 登录信息 + * @return 结果 + */ + @PostMapping("/login") + public AjaxResult login(@RequestBody LoginBody loginBody) { + AjaxResult ajax = AjaxResult.success(); + // 生成令牌 + String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(), + loginBody.getUuid()); + ajax.put(Constants.TOKEN, token); + return ajax; + } + + /** + * 获取用户信息 + * + * @return 用户信息 + */ + @GetMapping("getInfo") + public AjaxResult getInfo() { + SysUser user = SecurityUtils.getLoginUser().getUser(); + // 角色集合 + Set roles = permissionService.getRolePermission(user); + // 权限集合 + Set permissions = permissionService.getMenuPermission(user); + AjaxResult ajax = AjaxResult.success(); + ajax.put("user", user); + ajax.put("roles", roles); + ajax.put("permissions", permissions); + return ajax; + } + + /** + * 获取路由信息 + * + * @return 路由信息 + */ + @GetMapping("getRouters") + public AjaxResult getRouters() { + Long userId = SecurityUtils.getUserId(); + List menus = menuService.selectMenuTreeByUserId(userId); + return AjaxResult.success(menuService.buildMenus(menus)); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java new file mode 100644 index 00000000..9da3fa4f --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java @@ -0,0 +1,156 @@ +package com.ruoyi.web.controller.system; + +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.domain.entity.SysMenu; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.enums.CommonStatusEnum; +import com.ruoyi.common.mybatis.query.LambdaQueryWrapperX; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.domain.vo.menu.MenuSimpleRespVO; +import com.ruoyi.system.service.ISysMenuService; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +/** + * 菜单信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/menu") +public class SysMenuController extends BaseController { + @Autowired + private ISysMenuService menuService; + + /** + * 获取菜单列表 + */ + @PreAuthorize("@ss.hasPermi('system:menu:list')") + @GetMapping("/list") + public AjaxResult list(SysMenu menu) { + List menus = menuService.selectMenuList(menu, getUserId()); + return success(menus); + } + + /** + * 根据菜单编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:menu:query')") + @GetMapping(value = "/{menuId}") + public AjaxResult getInfo(@PathVariable Long menuId) { + return success(menuService.selectMenuById(menuId)); + } + + /** + * 获取菜单下拉树列表 + */ + @GetMapping("/treeselect") + public AjaxResult treeselect(SysMenu menu) { + List menus = menuService.selectMenuList(menu, getUserId()); + return success(menuService.buildMenuTreeSelect(menus)); + } + + /** + * 加载对应角色菜单列表树 + */ + @GetMapping(value = "/roleMenuTreeselect/{roleId}") + public AjaxResult roleMenuTreeselect(@PathVariable("roleId") Long roleId) { + List menus = menuService.selectMenuList(getUserId()); + AjaxResult ajax = success(); + ajax.put("checkedKeys", menuService.selectMenuListByRoleId(roleId)); + ajax.put("menus", menuService.buildMenuTreeSelect(menus)); + return ajax; + } + + /** + * 新增菜单 + */ + @PreAuthorize("@ss.hasPermi('system:menu:add')") + @Log(title = "菜单管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysMenu menu) { + if (UserConstants.NOT_UNIQUE.equals(menuService.checkMenuNameUnique(menu))) { + return error("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); + } else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) { + return error("新增菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头"); + } + menu.setCreateBy(getUsername()); + return toAjax(menuService.insertMenu(menu)); + } + + /** + * 修改菜单 + */ + @PreAuthorize("@ss.hasPermi('system:menu:edit')") + @Log(title = "菜单管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysMenu menu) { + if (UserConstants.NOT_UNIQUE.equals(menuService.checkMenuNameUnique(menu))) { + return error("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); + } else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) { + return error("修改菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头"); + } else if (menu.getMenuId().equals(menu.getParentId())) { + return error("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己"); + } + menu.setUpdateBy(getUsername()); + return toAjax(menuService.updateMenu(menu)); + } + + /** + * 删除菜单 + */ + @PreAuthorize("@ss.hasPermi('system:menu:remove')") + @Log(title = "菜单管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{menuId}") + public AjaxResult remove(@PathVariable("menuId") Long menuId) { + if (menuService.hasChildByMenuId(menuId)) { + return warn("存在子菜单,不允许删除"); + } + if (menuService.checkMenuExistRole(menuId)) { + return warn("菜单已分配,不允许删除"); + } + return toAjax(menuService.deleteMenuById(menuId)); + } + + @GetMapping("/list-all-simple") + @ApiOperation(value = "获取菜单精简信息列表", notes = "只包含被开启的菜单,用于【角色分配菜单】功能的选项。" + "在多租户的场景下,会只返回租户所在套餐有的菜单") + public R> getSimpleMenus() { + // 获得菜单列表,只要开启状态的 +// List list = menuService.list(new LambdaQueryWrapperX() +// .eqIfPresent(SysMenu::getStatus, "0")); + SysMenu sysMenu = new SysMenu(); +// sysMenu.setStatus("0"); +// sysMenu.setVisible("0"); + List list = menuService.getTenantMenus(sysMenu); + // 排序后,返回给前端 + list.sort(Comparator.comparing(SysMenu::getOrderNum)); + List results = new ArrayList<>(); + for (SysMenu menu : list) { + MenuSimpleRespVO vo = new MenuSimpleRespVO(); + vo.setId(menu.getMenuId()); + vo.setName(menu.getMenuName()); + vo.setParentId(menu.getParentId()); + String menuType = menu.getMenuType(); + if ("M".equals(menuType)) { + vo.setType(1); + }else if ("C".equals(menuType)) { + vo.setType(2); + }else if ("F".equals(menuType)) { + vo.setType(3); + } + results.add(vo); + } + return R.ok(results); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java new file mode 100644 index 00000000..61c72cf0 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java @@ -0,0 +1,79 @@ +package com.ruoyi.web.controller.system; + +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.system.domain.SysNotice; +import com.ruoyi.system.service.ISysNoticeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 公告 信息操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/notice") +public class SysNoticeController extends BaseController { + @Autowired + private ISysNoticeService noticeService; + + /** + * 获取通知公告列表 + */ + @PreAuthorize("@ss.hasPermi('system:notice:list')") + @GetMapping("/list") + public TableDataInfo list(SysNotice notice) { + startPage(); + List list = noticeService.selectNoticeList(notice); + return getDataTable(list); + } + + /** + * 根据通知公告编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:notice:query')") + @GetMapping(value = "/{noticeId}") + public AjaxResult getInfo(@PathVariable Long noticeId) { + return success(noticeService.selectNoticeById(noticeId)); + } + + /** + * 新增通知公告 + */ + @PreAuthorize("@ss.hasPermi('system:notice:add')") + @Log(title = "通知公告", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysNotice notice) { + notice.setCreateBy(getUsername()); + return toAjax(noticeService.insertNotice(notice)); + } + + /** + * 修改通知公告 + */ + @PreAuthorize("@ss.hasPermi('system:notice:edit')") + @Log(title = "通知公告", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysNotice notice) { + notice.setUpdateBy(getUsername()); + return toAjax(noticeService.updateNotice(notice)); + } + + /** + * 删除通知公告 + */ + @PreAuthorize("@ss.hasPermi('system:notice:remove')") + @Log(title = "通知公告", businessType = BusinessType.DELETE) + @DeleteMapping("/{noticeIds}") + public AjaxResult remove(@PathVariable Long[] noticeIds) { + return toAjax(noticeService.deleteNoticeByIds(noticeIds)); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java new file mode 100644 index 00000000..5beba7d0 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java @@ -0,0 +1,123 @@ +package com.ruoyi.web.controller.system; + +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.system.domain.SysPost; +import com.ruoyi.system.service.ISysPostService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletResponse; +import java.util.Comparator; +import java.util.List; + +/** + * 岗位信息操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/post") +public class SysPostController extends BaseController { + @Autowired + private ISysPostService postService; + + /** + * 获取岗位列表 + */ + @PreAuthorize("@ss.hasPermi('system:post:list')") + @GetMapping("/list") + public TableDataInfo list(SysPost post) { + startPage(); + List list = postService.selectPostList(post); + return getDataTable(list); + } + + @Log(title = "岗位管理", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:post:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysPost post) { + List list = postService.selectPostList(post); + ExcelUtil util = new ExcelUtil(SysPost.class); + util.exportExcel(response, list, "岗位数据"); + } + + /** + * 根据岗位编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:post:query')") + @GetMapping(value = "/{postId}") + public AjaxResult getInfo(@PathVariable Long postId) { + return success(postService.selectPostById(postId)); + } + + /** + * 新增岗位 + */ + @PreAuthorize("@ss.hasPermi('system:post:add')") + @Log(title = "岗位管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysPost post) { + if (UserConstants.NOT_UNIQUE.equals(postService.checkPostNameUnique(post))) { + return error("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在"); + } else if (UserConstants.NOT_UNIQUE.equals(postService.checkPostCodeUnique(post))) { + return error("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在"); + } + post.setCreateBy(getUsername()); + return toAjax(postService.insertPost(post)); + } + + /** + * 修改岗位 + */ + @PreAuthorize("@ss.hasPermi('system:post:edit')") + @Log(title = "岗位管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysPost post) { + if (UserConstants.NOT_UNIQUE.equals(postService.checkPostNameUnique(post))) { + return error("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在"); + } else if (UserConstants.NOT_UNIQUE.equals(postService.checkPostCodeUnique(post))) { + return error("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在"); + } + post.setUpdateBy(getUsername()); + return toAjax(postService.updatePost(post)); + } + + /** + * 删除岗位 + */ + @PreAuthorize("@ss.hasPermi('system:post:remove')") + @Log(title = "岗位管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{postIds}") + public AjaxResult remove(@PathVariable Long[] postIds) { + return toAjax(postService.deletePostByIds(postIds)); + } + + /** + * 获取岗位选择框列表 + */ + @GetMapping("/optionselect") + public AjaxResult optionselect() { + List posts = postService.selectPostAll(); + return success(posts); + } + + /** + * 获取岗位精简信息列表 + * 只包含被开启的岗位,主要用于前端的下拉选项 + */ + @GetMapping("/list-all-simple") + public AjaxResult getSimplePosts() { + List list = postService.selectPostList(new SysPost()); + // 排序后,返回给前端 + list.sort(Comparator.comparing(SysPost::getPostSort)); + return success(list); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java new file mode 100644 index 00000000..e3d10c0d --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java @@ -0,0 +1,125 @@ +package com.ruoyi.web.controller.system; + +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.file.FileUploadUtils; +import com.ruoyi.common.utils.file.MimeTypeUtils; +import com.ruoyi.framework.web.service.TokenService; +import com.ruoyi.system.service.ISysUserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +/** + * 个人信息 业务处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/user/profile") +public class SysProfileController extends BaseController { + @Autowired + private ISysUserService userService; + + @Autowired + private TokenService tokenService; + + /** + * 个人信息 + */ + @GetMapping + public AjaxResult profile() { + LoginUser loginUser = getLoginUser(); + SysUser user = loginUser.getUser(); + AjaxResult ajax = success(user); + ajax.put("roleGroup", userService.selectUserRoleGroup(loginUser.getUsername())); + ajax.put("postGroup", userService.selectUserPostGroup(loginUser.getUsername())); + return ajax; + } + + /** + * 修改用户 + */ + @Log(title = "个人信息", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult updateProfile(@RequestBody SysUser user) { + LoginUser loginUser = getLoginUser(); + SysUser sysUser = loginUser.getUser(); + user.setUserName(sysUser.getUserName()); + if (StringUtils.isNotEmpty(user.getPhonenumber()) + && UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user))) { + return error("修改用户'" + user.getUserName() + "'失败,手机号码已存在"); + } + if (StringUtils.isNotEmpty(user.getEmail()) + && UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user))) { + return error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + user.setUserId(sysUser.getUserId()); + user.setPassword(null); + user.setAvatar(null); + user.setDeptId(null); + if (userService.updateUserProfile(user) > 0) { + // 更新缓存用户信息 + sysUser.setNickName(user.getNickName()); + sysUser.setPhonenumber(user.getPhonenumber()); + sysUser.setEmail(user.getEmail()); + sysUser.setSex(user.getSex()); + tokenService.setLoginUser(loginUser); + return success(); + } + return error("修改个人信息异常,请联系管理员"); + } + + /** + * 重置密码 + */ + @Log(title = "个人信息", businessType = BusinessType.UPDATE) + @PutMapping("/updatePwd") + public AjaxResult updatePwd(String oldPassword, String newPassword) { + LoginUser loginUser = getLoginUser(); + String userName = loginUser.getUsername(); + String password = loginUser.getPassword(); + if (!SecurityUtils.matchesPassword(oldPassword, password)) { + return error("修改密码失败,旧密码错误"); + } + if (SecurityUtils.matchesPassword(newPassword, password)) { + return error("新密码不能与旧密码相同"); + } + if (userService.resetUserPwd(userName, SecurityUtils.encryptPassword(newPassword)) > 0) { + // 更新缓存用户密码 + loginUser.getUser().setPassword(SecurityUtils.encryptPassword(newPassword)); + tokenService.setLoginUser(loginUser); + return success(); + } + return error("修改密码异常,请联系管理员"); + } + + /** + * 头像上传 + */ + @Log(title = "用户头像", businessType = BusinessType.UPDATE) + @PostMapping("/avatar") + public AjaxResult avatar(@RequestParam("avatarfile") MultipartFile file) throws Exception { + if (!file.isEmpty()) { + LoginUser loginUser = getLoginUser(); + String avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION); + if (userService.updateUserAvatar(loginUser.getUsername(), avatar)) { + AjaxResult ajax = success(); + ajax.put("imgUrl", avatar); + // 更新缓存用户头像 + loginUser.getUser().setAvatar(avatar); + tokenService.setLoginUser(loginUser); + return ajax; + } + } + return error("上传图片异常,请联系管理员"); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java new file mode 100644 index 00000000..fe192492 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java @@ -0,0 +1,38 @@ +package com.ruoyi.web.controller.system; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.model.RegisterBody; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.web.service.SysRegisterService; +import com.ruoyi.system.service.ISysConfigService; + +/** + * 注册验证 + * + * @author ruoyi + */ +@RestController +public class SysRegisterController extends BaseController +{ + @Autowired + private SysRegisterService registerService; + + @Autowired + private ISysConfigService configService; + + @PostMapping("/register") + public AjaxResult register(@RequestBody RegisterBody user) + { + if (!("true".equals(configService.selectConfigByKey("sys.account.registerUser")))) + { + return error("当前系统没有开启注册功能!"); + } + String msg = registerService.register(user); + return StringUtils.isEmpty(msg) ? success() : error(msg); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java new file mode 100644 index 00000000..64dc2adf --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java @@ -0,0 +1,267 @@ +package com.ruoyi.web.controller.system; + +import cn.hutool.core.collection.CollUtil; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysDept; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.framework.web.service.DataScopeService; +import com.ruoyi.framework.web.service.SysPermissionService; +import com.ruoyi.framework.web.service.TokenService; +import com.ruoyi.system.domain.SysUserRole; +import com.ruoyi.system.service.ISysDeptService; +import com.ruoyi.system.service.ISysRoleService; +import com.ruoyi.system.service.ISysUserService; +import com.ruoyi.system.service.TenantService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 角色信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/role") +public class SysRoleController extends BaseController { + @Autowired + private ISysRoleService roleService; + + @Autowired + private TokenService tokenService; + + @Autowired + private SysPermissionService permissionService; + + @Autowired + private ISysUserService userService; + + @Autowired + private ISysDeptService deptService; + + @Autowired + private DataScopeService dataScopeService; + @Resource + @Lazy // 延迟,避免循环依赖报错 + private TenantService tenantService; + + @PreAuthorize("@ss.hasPermi('system:role:list')") + @GetMapping("/list") + public TableDataInfo list(SysRole role) { + startPage(); + List list = roleService.selectRoleList(role); + return getDataTable(list); + } + + @Log(title = "角色管理", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:role:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysRole role) { + List list = roleService.selectRoleList(role); + ExcelUtil util = new ExcelUtil(SysRole.class); + util.exportExcel(response, list, "角色数据"); + } + + /** + * 根据角色编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:role:query')") + @GetMapping(value = "/{roleId}") + public AjaxResult getInfo(@PathVariable Long roleId) { + roleService.checkRoleDataScope(roleId); + return success(roleService.selectRoleById(roleId)); + } + + /** + * 新增角色 + */ + @PreAuthorize("@ss.hasPermi('system:role:add')") + @Log(title = "角色管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysRole role) { + if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleNameUnique(role))) { + return error("新增角色'" + role.getRoleName() + "'失败,角色名称已存在"); + } else if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleKeyUnique(role))) { + return error("新增角色'" + role.getRoleName() + "'失败,角色权限已存在"); + } + role.setCreateBy(getUsername()); + // 开启多租户的情况下,需要过滤掉未开通的菜单 + Set tmids = Arrays.stream(role.getMenuIds()).collect(Collectors.toSet()); + tenantService.handleTenantMenu(menuIds -> tmids.removeIf(menuId -> !CollUtil.contains(menuIds, menuId))); + role.setMenuIds(tmids.toArray(new Long[tmids.size()])); + return toAjax(roleService.insertRole(role)); + + } + + /** + * 修改保存角色 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysRole role) { + roleService.checkRoleAllowed(role); + roleService.checkRoleDataScope(role.getRoleId()); + // 开启多租户的情况下,需要过滤掉未开通的菜单 + Set tmids = Arrays.stream(role.getMenuIds()).collect(Collectors.toSet()); + tenantService.handleTenantMenu(menuIds -> tmids.removeIf(menuId -> !CollUtil.contains(menuIds, menuId))); + role.setMenuIds(tmids.toArray(new Long[tmids.size()])); + if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleNameUnique(role))) { + return error("修改角色'" + role.getRoleName() + "'失败,角色名称已存在"); + } else if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleKeyUnique(role))) { + return error("修改角色'" + role.getRoleName() + "'失败,角色权限已存在"); + } + role.setUpdateBy(getUsername()); + + if (roleService.updateRole(role) > 0) { + // 更新缓存用户权限 + LoginUser loginUser = getLoginUser(); + if (StringUtils.isNotNull(loginUser.getUser()) && !loginUser.getUser().isAdmin()) { + loginUser.setPermissions(permissionService.getMenuPermission(loginUser.getUser())); + loginUser.setUser(userService.selectUserByUserName(loginUser.getUser().getUserName())); + loginUser.setDataScopes(dataScopeService.getUserDataScopeIdList(loginUser.getUserId(), loginUser.getDeptId())); + tokenService.setLoginUser(loginUser); + } + return success(); + } + return error("修改角色'" + role.getRoleName() + "'失败,请联系管理员"); + } + + /** + * 修改保存数据权限 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping("/dataScope") + public AjaxResult dataScope(@RequestBody SysRole role) { + roleService.checkRoleAllowed(role); + roleService.checkRoleDataScope(role.getRoleId()); + return toAjax(roleService.authDataScope(role)); + } + + /** + * 状态修改 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public AjaxResult changeStatus(@RequestBody SysRole role) { + roleService.checkRoleAllowed(role); + roleService.checkRoleDataScope(role.getRoleId()); + role.setUpdateBy(getUsername()); + return toAjax(roleService.updateRoleStatus(role)); + } + + /** + * 删除角色 + */ + @PreAuthorize("@ss.hasPermi('system:role:remove')") + @Log(title = "角色管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{roleIds}") + public AjaxResult remove(@PathVariable Long[] roleIds) { + return toAjax(roleService.deleteRoleByIds(roleIds)); + } + + /** + * 获取角色选择框列表 + */ + @PreAuthorize("@ss.hasPermi('system:role:query')") + @GetMapping("/optionselect") + public AjaxResult optionselect() { + return success(roleService.selectRoleAll()); + } + + /** + * 查询已分配用户角色列表 + */ + @PreAuthorize("@ss.hasPermi('system:role:list')") + @GetMapping("/authUser/allocatedList") + public TableDataInfo allocatedList(SysUser user) { + startPage(); + List list = userService.selectAllocatedList(user); + return getDataTable(list); + } + + /** + * 查询未分配用户角色列表 + */ + @PreAuthorize("@ss.hasPermi('system:role:list')") + @GetMapping("/authUser/unallocatedList") + public TableDataInfo unallocatedList(SysUser user) { + startPage(); + List list = userService.selectUnallocatedList(user); + return getDataTable(list); + } + + /** + * 取消授权用户 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/cancel") + public AjaxResult cancelAuthUser(@RequestBody SysUserRole userRole) { + return toAjax(roleService.deleteAuthUser(userRole)); + } + + /** + * 批量取消授权用户 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/cancelAll") + public AjaxResult cancelAuthUserAll(Long roleId, Long[] userIds) { + return toAjax(roleService.deleteAuthUsers(roleId, userIds)); + } + + /** + * 批量选择用户授权 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/selectAll") + public AjaxResult selectAuthUserAll(Long roleId, Long[] userIds) { + roleService.checkRoleDataScope(roleId); + return toAjax(roleService.insertAuthUsers(roleId, userIds)); + } + + /** + * 获取对应角色部门树列表 + */ + @PreAuthorize("@ss.hasPermi('system:role:query')") + @GetMapping(value = "/deptTree/{roleId}") + public AjaxResult deptTree(@PathVariable("roleId") Long roleId) { + AjaxResult ajax = success(); + ajax.put("checkedKeys", deptService.selectDeptListByRoleId(roleId)); + ajax.put("depts", deptService.selectDeptTreeList(new SysDept())); + return ajax; + } + + /** + * 获取角色精简信息列表 + * 只包含被开启的角色,主要用于前端的下拉选项 + */ + @GetMapping("/list-all-simple") + public AjaxResult getSimpleRoles() { + List list = roleService.selectRoleList(new SysRole()); + // 排序后,返回给前端 + list.sort(Comparator.comparing(SysRole::getRoleSort)); + return success(list); + } + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysTreeDictController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysTreeDictController.java new file mode 100644 index 00000000..e0eeeb3e --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysTreeDictController.java @@ -0,0 +1,84 @@ +package com.ruoyi.web.controller.system; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.system.domain.SysTreeDict; +import com.ruoyi.system.service.ISysTreeDictService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 系统树形Controller + * + * @author wangzongrun + * @date 2021-06-04 + */ +@RestController +@RequestMapping("system/dict/tree") +public class SysTreeDictController extends BaseController { + + @Autowired + private ISysTreeDictService SysTreeDictService; + + /** + * 分页查询系统树形列表 + */ + @GetMapping("/list") + public TableDataInfo list(SysTreeDict sysTreeDict) { + IPage list = SysTreeDictService.selectList(getPage(), sysTreeDict); + return getDataTable(list); + } + + /** + * 查询字典类型详细 + */ + @GetMapping(value = "/{dictId}") + public AjaxResult getInfo(@PathVariable String dictId) { + return success(SysTreeDictService.selectById(dictId)); + } + + /** + * 导出系统树形列表 + */ + @Log(title = "系统树形", businessType = BusinessType.EXPORT) + @GetMapping("/export") + public AjaxResult export(SysTreeDict sysTreeDict) { + List list = SysTreeDictService.selectListAll(sysTreeDict); + ExcelUtil util = new ExcelUtil(SysTreeDict.class); + return util.exportExcel(list, "系统树形"); + } + + /** + * 新增系统树形 + */ + @Log(title = "系统树形", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody SysTreeDict sysTreeDict) { + return toAjax(SysTreeDictService.insert(sysTreeDict)); + } + + /** + * 修改系统树形 + */ + @Log(title = "系统树形", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody SysTreeDict sysTreeDict) { + return toAjax(SysTreeDictService.update(sysTreeDict)); + } + + /** + * 删除系统树形 + */ + @Log(title = "系统树形", businessType = BusinessType.DELETE) + @DeleteMapping("/{sids}") + public AjaxResult remove(@PathVariable String[] sids) { + return toAjax(SysTreeDictService.deleteByIds(sids)); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysTreeDictDataController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysTreeDictDataController.java new file mode 100644 index 00000000..2cfad468 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysTreeDictDataController.java @@ -0,0 +1,87 @@ +package com.ruoyi.web.controller.system; + +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.system.domain.SysTreeDictData; +import com.ruoyi.system.service.ISysTreeDictDataService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 树形字典数据Controller + * + * @author wangzongrun + * @date 2021-05-31 + */ +@RestController +@RequestMapping("system/tree/dict/data") +public class SysTreeDictDataController extends BaseController { + + @Autowired + private ISysTreeDictDataService sysTreeDictDataService; + + /** + * 分页查询树形字典数据列表 + */ + @GetMapping("list") + public AjaxResult list(SysTreeDictData sysTreeDictData) { + List list = sysTreeDictDataService.buildTree(sysTreeDictData); + return success(list); + } + + /** + * 查询树形字典数据列表 + */ + @GetMapping("/tree") + public AjaxResult tree(SysTreeDictData sysTreeDictData) { + List list = sysTreeDictDataService.buildTree(sysTreeDictData); + return success(list); + } + + /** + * 新增树形字典数据 + */ + @Log(title = "树形字典数据", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody SysTreeDictData sysTreeDictData) { + return toAjax(sysTreeDictDataService.insert(sysTreeDictData)); + } + + /** + * 修改树形字典数据 + */ + @Log(title = "树形字典数据", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody SysTreeDictData sysTreeDictData) { + return toAjax(sysTreeDictDataService.update(sysTreeDictData)); + } + + /** + * 删除树形字典数据 + */ + @Log(title = "树形字典数据", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable String[] ids) { + return toAjax(sysTreeDictDataService.deleteByIds(ids)); + } + + /** + * 检查名称唯一性 + */ + @PostMapping(value = "/check/unique/label") + public AjaxResult checkUniqueByLabel(@RequestBody SysTreeDictData sysTreeDictData) { + return sysTreeDictDataService.checkUniqueByLabel(sysTreeDictData); + } + + /** + * 校验编码 + */ + @PostMapping(value = "/check/code") + public AjaxResult checkCode(@RequestBody SysTreeDictData sysTreeDictData) { + return sysTreeDictDataService.checkCode(sysTreeDictData); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java new file mode 100644 index 00000000..590ad98b --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java @@ -0,0 +1,260 @@ +package com.ruoyi.web.controller.system; + +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysDept; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.enums.CommonStatusEnum; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.system.convert.user.UserConvert; +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 org.apache.commons.lang3.ArrayUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletResponse; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 用户信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/user") +public class SysUserController extends BaseController { + @Autowired + private ISysUserService userService; + + @Autowired + private ISysRoleService roleService; + + @Autowired + private ISysDeptService deptService; + + @Autowired + private ISysPostService postService; + + /** + * 获取用户列表 + */ + @PreAuthorize("@ss.hasPermi('system:user:list')") + @GetMapping("/list") + public TableDataInfo list(SysUser user) { + startPage(); + List list = userService.selectUserList(user); + return getDataTable(list); + } + /** + * 获取用户列表 + */ + @PreAuthorize("@ss.hasPermi('system:user:xalist')") + @GetMapping("/xalist") + public TableDataInfo xalist(SysUser user) { + SysRole xiaoan = roleService.selectByRoleKey("xiaoan"); + if (xiaoan == null) { + return getDataTable(new ArrayList<>()); + } + startPage(); + user.setRoleId(xiaoan.getRoleId()); //角色 + List list = userService.selectUserList(user); + return getDataTable(list); + } + + @Log(title = "用户管理", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:user:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysUser user) { + List list = userService.selectUserList(user); + ExcelUtil util = new ExcelUtil(SysUser.class); + util.exportExcel(response, list, "用户数据"); + } + + @Log(title = "用户管理", businessType = BusinessType.IMPORT) + @PreAuthorize("@ss.hasPermi('system:user:import')") + @PostMapping("/importData") + public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception { + ExcelUtil util = new ExcelUtil(SysUser.class); + List userList = util.importExcel(file.getInputStream()); + String operName = getUsername(); + String message = userService.importUser(userList, updateSupport, operName); + return success(message); + } + + @PostMapping("/importTemplate") + public void importTemplate(HttpServletResponse response) { + ExcelUtil util = new ExcelUtil(SysUser.class); + util.importTemplateExcel(response, "用户数据"); + } + + /** + * 根据用户编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:user:query')") + @GetMapping(value = {"/", "/{userId}"}) + public AjaxResult getInfo(@PathVariable(value = "userId", required = false) Long userId) { + userService.checkUserDataScope(userId); + AjaxResult ajax = success(); + List roles = roleService.selectRoleAll(); + ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList())); + ajax.put("posts", postService.selectPostAll()); + if (StringUtils.isNotNull(userId)) { + SysUser sysUser = userService.selectUserById(userId); + ajax.put(AjaxResult.DATA_TAG, sysUser); + ajax.put("postIds", postService.selectPostListByUserId(userId)); + ajax.put("roleIds", sysUser.getRoles().stream().map(SysRole::getRoleId).collect(Collectors.toList())); + } + return ajax; + } + + /** + * 新增用户 + */ + @PreAuthorize("@ss.hasPermi('system:user:add')") + @Log(title = "用户管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysUser user) { + if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(user))) { + return error("新增用户'" + user.getUserName() + "'失败,登录账号已存在"); + } else if (StringUtils.isNotEmpty(user.getPhonenumber()) + && UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user))) { + return error("新增用户'" + user.getUserName() + "'失败,手机号码已存在"); + } else if (StringUtils.isNotEmpty(user.getEmail()) + && UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user))) { + return error("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + if (user.getRoleIds() == null || user.getRoleIds().length ==0) { + return error("角色不能为空,请分配角色"); + } + user.setCreateBy(getUsername()); + user.setPassword(SecurityUtils.encryptPassword(user.getPassword())); + return toAjax(userService.insertUser(user)); + } + + /** + * 修改用户 + */ + @PreAuthorize("@ss.hasPermi('system:user:edit')") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysUser user) { + userService.checkUserAllowed(user); + userService.checkUserDataScope(user.getUserId()); + if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(user))) { + return error("修改用户'" + user.getUserName() + "'失败,登录账号已存在"); + } else if (StringUtils.isNotEmpty(user.getPhonenumber()) + && UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user))) { + return error("修改用户'" + user.getUserName() + "'失败,手机号码已存在"); + } else if (StringUtils.isNotEmpty(user.getEmail()) + && UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user))) { + return error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + if (user.getRoleIds() == null || user.getRoleIds().length ==0) { + return error("角色不能为空,请分配角色"); + } + user.setUpdateBy(getUsername()); + return toAjax(userService.updateUser(user)); + } + + /** + * 删除用户 + */ + @PreAuthorize("@ss.hasPermi('system:user:remove')") + @Log(title = "用户管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{userIds}") + public AjaxResult remove(@PathVariable Long[] userIds) { + if (ArrayUtils.contains(userIds, getUserId())) { + return error("当前用户不能删除"); + } + return toAjax(userService.deleteUserByIds(userIds)); + } + + /** + * 重置密码 + */ + @PreAuthorize("@ss.hasPermi('system:user:resetPwd')") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping("/resetPwd") + public AjaxResult resetPwd(@RequestBody SysUser user) { + userService.checkUserAllowed(user); + userService.checkUserDataScope(user.getUserId()); + user.setPassword(SecurityUtils.encryptPassword(user.getPassword())); + user.setUpdateBy(getUsername()); + return toAjax(userService.resetPwd(user)); + } + + /** + * 状态修改 + */ + @PreAuthorize("@ss.hasPermi('system:user:edit')") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public AjaxResult changeStatus(@RequestBody SysUser user) { + userService.checkUserAllowed(user); + userService.checkUserDataScope(user.getUserId()); + user.setUpdateBy(getUsername()); + return toAjax(userService.updateUserStatus(user)); + } + + /** + * 根据用户编号获取授权角色 + */ + @PreAuthorize("@ss.hasPermi('system:user:query')") + @GetMapping("/authRole/{userId}") + public AjaxResult authRole(@PathVariable("userId") Long userId) { + AjaxResult ajax = success(); + SysUser user = userService.selectUserById(userId); + List roles = roleService.selectRolesByUserId(userId); + ajax.put("user", user); + ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList())); + return ajax; + } + + /** + * 用户授权角色 + */ + @PreAuthorize("@ss.hasPermi('system:user:edit')") + @Log(title = "用户管理", businessType = BusinessType.GRANT) + @PutMapping("/authRole") + public AjaxResult insertAuthRole(Long userId, Long[] roleIds) { + userService.checkUserDataScope(userId); + userService.insertUserAuth(userId, roleIds); + return success(); + } + + /** + * 获取部门树列表 + */ + @PreAuthorize("@ss.hasPermi('system:user:list')") + @GetMapping("/deptTree") + public AjaxResult deptTree(SysDept dept) { + return success(deptService.selectDeptTreeList(dept)); + } + + /** + * 获取用户精简信息列表 + * 只包含被开启的用户,主要用于前端的下拉选项 + */ + @GetMapping("/list-all-simple") + public AjaxResult getSimpleUsers() { + // 获用户列表,只要开启状态的 + List list = userService.getUserListByStatus(CommonStatusEnum.ENABLE.getStatus()); + // 排序后,返回给前端 + return success(UserConvert.INSTANCE.convertList04(list)); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/TenantController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/TenantController.java new file mode 100644 index 00000000..3ee24562 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/TenantController.java @@ -0,0 +1,96 @@ +package com.ruoyi.web.controller.system; + + +import static com.ruoyi.common.core.domain.R.*; + +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.common.utils.ExcelUtils; +import com.ruoyi.system.convert.tenant.TenantConvert; +import com.ruoyi.system.domain.TenantDO; +import com.ruoyi.system.domain.vo.tenant.*; +import com.ruoyi.system.service.TenantService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.annotation.security.PermitAll; +import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; +import java.io.IOException; +import java.util.List; + + +@Api(tags = "管理后台 - 租户") +@RestController +@RequestMapping("/system/tenant") +public class TenantController { + + @Resource + private TenantService tenantService; + + @GetMapping("/get-id-by-name") + @PermitAll + @ApiOperation(value = "使用租户名,获得租户编号", notes = "登录界面,根据用户的租户名,获得租户编号") + @ApiImplicitParam(name = "name", value = "租户名", required = true, example = "1024", dataTypeClass = Long.class) + public R getTenantIdByName(@RequestParam("name") String name) { + TenantDO tenantDO = tenantService.getTenantByName(name); + return ok(tenantDO != null ? tenantDO.getId() : null); + } + + @PostMapping("/create") + @ApiOperation("创建租户") + @PreAuthorize("@ss.hasPermi('system:tenant:create')") + public R createTenant(@Valid @RequestBody TenantCreateReqVO createReqVO) { + return ok(tenantService.createTenant(createReqVO)); + } + + @PutMapping("/update") + @ApiOperation("更新租户") + @PreAuthorize("@ss.hasPermi('system:tenant:update')") + public R updateTenant(@Valid @RequestBody TenantUpdateReqVO updateReqVO) { + tenantService.updateTenant(updateReqVO); + return ok(true); + } + + @DeleteMapping("/delete") + @ApiOperation("删除租户") + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermi('system:tenant:delete')") + public R deleteTenant(@RequestParam("id") Long id) { + tenantService.deleteTenant(id); + return ok(true); + } + + @GetMapping("/get") + @ApiOperation("获得租户") + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermi('system:tenant:query')") + public R getTenant(@RequestParam("id") Long id) { + TenantDO tenant = tenantService.getTenant(id); + return ok(TenantConvert.INSTANCE.convert(tenant)); + } + + @GetMapping("/page") + @ApiOperation("获得租户分页") + @PreAuthorize("@ss.hasPermi('system:tenant:query')") + public R> getTenantPage(@Valid TenantPageReqVO pageVO) { + PageResult pageResult = tenantService.getTenantPage(pageVO); + return ok(TenantConvert.INSTANCE.convertPage(pageResult)); + } + + @GetMapping("/export-excel") + @ApiOperation("导出租户 Excel") + @PreAuthorize("@ss.hasPermi('system:tenant:export')") + public void exportTenantExcel(@Valid TenantExportReqVO exportReqVO, HttpServletResponse response) throws IOException { + List list = tenantService.getTenantList(exportReqVO); + // 导出 Excel + List datas = TenantConvert.INSTANCE.convertList02(list); + ExcelUtils.write(response, "租户.xls", "数据", TenantExcelVO.class, datas); + } + + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/TenantPackageController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/TenantPackageController.java new file mode 100644 index 00000000..06f616a6 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/TenantPackageController.java @@ -0,0 +1,83 @@ +package com.ruoyi.web.controller.system; + + +import static com.ruoyi.common.core.domain.R.*; + +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.enums.CommonStatusEnum; +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.system.convert.tenant.TenantPackageConvert; +import com.ruoyi.system.domain.TenantPackageDO; +import com.ruoyi.system.domain.vo.packages.*; +import com.ruoyi.system.service.TenantPackageService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.List; + + +@Api(tags = "管理后台 - 租户套餐") +@RestController +@RequestMapping("/system/tenant-package") +@Validated +public class TenantPackageController { + + @Resource + private TenantPackageService tenantPackageService; + + @PostMapping("/create") + @ApiOperation("创建租户套餐") + @PreAuthorize("@ss.hasPermi('system:tenant-package:create')") + public R createTenantPackage(@Valid @RequestBody TenantPackageCreateReqVO createReqVO) { + return ok(tenantPackageService.createTenantPackage(createReqVO)); + } + + @PutMapping("/update") + @ApiOperation("更新租户套餐") + @PreAuthorize("@ss.hasPermi('system:tenant-package:update')") + public R updateTenantPackage(@Valid @RequestBody TenantPackageUpdateReqVO updateReqVO) { + tenantPackageService.updateTenantPackage(updateReqVO); + return ok(true); + } + + @DeleteMapping("/delete") + @ApiOperation("删除租户套餐") + @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermi('system:tenant-package:delete')") + public R deleteTenantPackage(@RequestParam("id") Long id) { + tenantPackageService.deleteTenantPackage(id); + return ok(true); + } + + @GetMapping("/get") + @ApiOperation("获得租户套餐") + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermi('system:tenant-package:query')") + public R getTenantPackage(@RequestParam("id") Long id) { + TenantPackageDO tenantPackage = tenantPackageService.getTenantPackage(id); + return ok(TenantPackageConvert.INSTANCE.convert(tenantPackage)); + } + + @GetMapping("/page") + @ApiOperation("获得租户套餐分页") + @PreAuthorize("@ss.hasPermi('system:tenant-package:query')") + public R> getTenantPackagePage(@Valid TenantPackagePageReqVO pageVO) { + PageResult pageResult = tenantPackageService.getTenantPackagePage(pageVO); + return ok(TenantPackageConvert.INSTANCE.convertPage(pageResult)); + } + + @GetMapping("/get-simple-list") + @ApiOperation(value = "获取租户套餐精简信息列表", notes = "只包含被开启的租户套餐,主要用于前端的下拉选项") + public R> getTenantPackageList() { + // 获得角色列表,只要开启状态的 + List list = tenantPackageService.getTenantPackageListByStatus(CommonStatusEnum.ENABLE.getStatus()); + return ok(TenantPackageConvert.INSTANCE.convertList02(list)); + } + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/SwaggerController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/SwaggerController.java new file mode 100644 index 00000000..f66ca24e --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/SwaggerController.java @@ -0,0 +1,24 @@ +package com.ruoyi.web.controller.tool; + +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import com.ruoyi.common.core.controller.BaseController; + +/** + * swagger 接口 + * + * @author ruoyi + */ +@Controller +@RequestMapping("/tool/swagger") +public class SwaggerController extends BaseController +{ + @PreAuthorize("@ss.hasPermi('tool:swagger:view')") + @GetMapping() + public String index() + { + return redirect("/swagger-ui.html"); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/TestController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/TestController.java new file mode 100644 index 00000000..b4f6bac7 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/TestController.java @@ -0,0 +1,183 @@ +package com.ruoyi.web.controller.tool; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.StringUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import io.swagger.annotations.ApiOperation; + +/** + * swagger 用户测试方法 + * + * @author ruoyi + */ +@Api("用户信息管理") +@RestController +@RequestMapping("/test/user") +public class TestController extends BaseController +{ + private final static Map users = new LinkedHashMap(); + { + users.put(1, new UserEntity(1, "admin", "admin123", "15888888888")); + users.put(2, new UserEntity(2, "ry", "admin123", "15666666666")); + } + + @ApiOperation("获取用户列表") + @GetMapping("/list") + public R> userList() + { + List userList = new ArrayList(users.values()); + return R.ok(userList); + } + + @ApiOperation("获取用户详细") + @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class) + @GetMapping("/{userId}") + public R getUser(@PathVariable Integer userId) + { + if (!users.isEmpty() && users.containsKey(userId)) + { + return R.ok(users.get(userId)); + } + else + { + return R.fail("用户不存在"); + } + } + + @ApiOperation("新增用户") + @ApiImplicitParams({ + @ApiImplicitParam(name = "userId", value = "用户id", dataType = "Integer", dataTypeClass = Integer.class), + @ApiImplicitParam(name = "username", value = "用户名称", dataType = "String", dataTypeClass = String.class), + @ApiImplicitParam(name = "password", value = "用户密码", dataType = "String", dataTypeClass = String.class), + @ApiImplicitParam(name = "mobile", value = "用户手机", dataType = "String", dataTypeClass = String.class) + }) + @PostMapping("/save") + public R save(UserEntity user) + { + if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId())) + { + return R.fail("用户ID不能为空"); + } + users.put(user.getUserId(), user); + return R.ok(); + } + + @ApiOperation("更新用户") + @PutMapping("/update") + public R update(@RequestBody UserEntity user) + { + if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId())) + { + return R.fail("用户ID不能为空"); + } + if (users.isEmpty() || !users.containsKey(user.getUserId())) + { + return R.fail("用户不存在"); + } + users.remove(user.getUserId()); + users.put(user.getUserId(), user); + return R.ok(); + } + + @ApiOperation("删除用户信息") + @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class) + @DeleteMapping("/{userId}") + public R delete(@PathVariable Integer userId) + { + if (!users.isEmpty() && users.containsKey(userId)) + { + users.remove(userId); + return R.ok(); + } + else + { + return R.fail("用户不存在"); + } + } +} + +@ApiModel(value = "UserEntity", description = "用户实体") +class UserEntity +{ + @ApiModelProperty("用户ID") + private Integer userId; + + @ApiModelProperty("用户名称") + private String username; + + @ApiModelProperty("用户密码") + private String password; + + @ApiModelProperty("用户手机") + private String mobile; + + public UserEntity() + { + + } + + public UserEntity(Integer userId, String username, String password, String mobile) + { + this.userId = userId; + this.username = username; + this.password = password; + this.mobile = mobile; + } + + public Integer getUserId() + { + return userId; + } + + public void setUserId(Integer userId) + { + this.userId = userId; + } + + public String getUsername() + { + return username; + } + + public void setUsername(String username) + { + this.username = username; + } + + public String getPassword() + { + return password; + } + + public void setPassword(String password) + { + this.password = password; + } + + public String getMobile() + { + return mobile; + } + + public void setMobile(String mobile) + { + this.mobile = mobile; + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java b/ruoyi-admin/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java new file mode 100644 index 00000000..3901ec6d --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java @@ -0,0 +1,125 @@ +package com.ruoyi.web.core.config; + +import java.util.ArrayList; +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import com.ruoyi.common.config.RuoYiConfig; +import io.swagger.annotations.ApiOperation; +import io.swagger.models.auth.In; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.ApiKey; +import springfox.documentation.service.AuthorizationScope; +import springfox.documentation.service.Contact; +import springfox.documentation.service.SecurityReference; +import springfox.documentation.service.SecurityScheme; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spi.service.contexts.SecurityContext; +import springfox.documentation.spring.web.plugins.Docket; + +/** + * Swagger2的接口配置 + * + * @author ruoyi + */ +@Configuration +public class SwaggerConfig +{ + /** 系统基础配置 */ + @Autowired + private RuoYiConfig ruoyiConfig; + + /** 是否开启swagger */ + @Value("${swagger.enabled}") + private boolean enabled; + + /** 设置请求的统一前缀 */ + @Value("${swagger.pathMapping}") + private String pathMapping; + + /** + * 创建API + */ + @Bean + public Docket createRestApi() + { + return new Docket(DocumentationType.OAS_30) + // 是否启用Swagger + .enable(enabled) + // 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息) + .apiInfo(apiInfo()) + // 设置哪些接口暴露给Swagger展示 + .select() + // 扫描所有有注解的api,用这种方式更灵活 + .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) + // 扫描指定包中的swagger注解 + // .apis(RequestHandlerSelectors.basePackage("com.ruoyi.project.tool.swagger")) + // 扫描所有 .apis(RequestHandlerSelectors.any()) + .paths(PathSelectors.any()) + .build() + /* 设置安全模式,swagger可以设置访问token */ + .securitySchemes(securitySchemes()) + .securityContexts(securityContexts()) + .pathMapping(pathMapping); + } + + /** + * 安全模式,这里指定token通过Authorization头请求头传递 + */ + private List securitySchemes() + { + List apiKeyList = new ArrayList(); + apiKeyList.add(new ApiKey("Authorization", "Authorization", In.HEADER.toValue())); + return apiKeyList; + } + + /** + * 安全上下文 + */ + private List securityContexts() + { + List securityContexts = new ArrayList<>(); + securityContexts.add( + SecurityContext.builder() + .securityReferences(defaultAuth()) + .operationSelector(o -> o.requestMappingPattern().matches("/.*")) + .build()); + return securityContexts; + } + + /** + * 默认的安全上引用 + */ + private List defaultAuth() + { + AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); + AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; + authorizationScopes[0] = authorizationScope; + List securityReferences = new ArrayList<>(); + securityReferences.add(new SecurityReference("Authorization", authorizationScopes)); + return securityReferences; + } + + /** + * 添加摘要信息 + */ + private ApiInfo apiInfo() + { + // 用ApiInfoBuilder进行定制 + return new ApiInfoBuilder() + // 设置标题 + .title("标题:数字消防综合管理系统_接口文档") + // 描述 + .description("描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...") + // 作者信息 + .contact(new Contact(ruoyiConfig.getName(), null, null)) + // 版本 + .version("版本号:" + ruoyiConfig.getVersion()) + .build(); + } +} diff --git a/ruoyi-admin/src/main/resources/META-INF/spring-devtools.properties b/ruoyi-admin/src/main/resources/META-INF/spring-devtools.properties new file mode 100644 index 00000000..2b23f85a --- /dev/null +++ b/ruoyi-admin/src/main/resources/META-INF/spring-devtools.properties @@ -0,0 +1 @@ +restart.include.json=/com.alibaba.fastjson.*.jar \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/application-dev.yml b/ruoyi-admin/src/main/resources/application-dev.yml new file mode 100644 index 00000000..0683a1ef --- /dev/null +++ b/ruoyi-admin/src/main/resources/application-dev.yml @@ -0,0 +1,63 @@ +ruoyi: + # 文件路径 + profile: D:/ruoyi/uploadPath +# 数据源配置 +spring: + datasource: + type: com.alibaba.druid.pool.DruidDataSource + driverClassName: com.mysql.cj.jdbc.Driver + druid: + # 主库数据源 + master: +# url: jdbc:mysql://121.40.191.30:3306/ry-base-tenant?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + url: jdbc:mysql://43.139.7.36:3306/ry-base-tenant?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + username: root + password: it@luokeng +# password: 123456 + + # 从库数据源 + slave: + # 从数据源开关/默认关闭 + enabled: false + url: + username: + password: + # 初始连接数 + initialSize: 5 + # 最小连接池数量 + minIdle: 10 + # 最大连接池数量 + maxActive: 20 + # 配置获取连接等待超时的时间 + maxWait: 60000 + # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 + timeBetweenEvictionRunsMillis: 60000 + # 配置一个连接在池中最小生存的时间,单位是毫秒 + minEvictableIdleTimeMillis: 300000 + # 配置一个连接在池中最大生存的时间,单位是毫秒 + maxEvictableIdleTimeMillis: 900000 + # 配置检测连接是否有效 + validationQuery: SELECT 1 FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + webStatFilter: + enabled: true + statViewServlet: + enabled: true + # 设置白名单,不填则允许所有访问 + allow: + url-pattern: /druid/* + # 控制台管理用户名和密码 + login-username: ruoyi + login-password: 123456 + filter: + stat: + enabled: true + # 慢SQL记录 + log-slow-sql: true + slow-sql-millis: 1000 + merge-sql: true + wall: + config: + multi-statement-allow: true diff --git a/ruoyi-admin/src/main/resources/application-druid.yml b/ruoyi-admin/src/main/resources/application-druid.yml new file mode 100644 index 00000000..b4aa52c5 --- /dev/null +++ b/ruoyi-admin/src/main/resources/application-druid.yml @@ -0,0 +1,57 @@ +# 数据源配置 +spring: + datasource: + type: com.alibaba.druid.pool.DruidDataSource + driverClassName: com.mysql.cj.jdbc.Driver + druid: + # 主库数据源 + master: + url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + username: root + password: root + # 从库数据源 + slave: + # 从数据源开关/默认关闭 + enabled: false + url: + username: + password: + # 初始连接数 + initialSize: 5 + # 最小连接池数量 + minIdle: 10 + # 最大连接池数量 + maxActive: 20 + # 配置获取连接等待超时的时间 + maxWait: 60000 + # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 + timeBetweenEvictionRunsMillis: 60000 + # 配置一个连接在池中最小生存的时间,单位是毫秒 + minEvictableIdleTimeMillis: 300000 + # 配置一个连接在池中最大生存的时间,单位是毫秒 + maxEvictableIdleTimeMillis: 900000 + # 配置检测连接是否有效 + validationQuery: SELECT 1 FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + webStatFilter: + enabled: true + statViewServlet: + enabled: true + # 设置白名单,不填则允许所有访问 + allow: + url-pattern: /druid/* + # 控制台管理用户名和密码 + login-username: ruoyi + login-password: 123456 + filter: + stat: + enabled: true + # 慢SQL记录 + log-slow-sql: true + slow-sql-millis: 1000 + merge-sql: true + wall: + config: + multi-statement-allow: true diff --git a/ruoyi-admin/src/main/resources/application-test.yml b/ruoyi-admin/src/main/resources/application-test.yml new file mode 100644 index 00000000..8217380e --- /dev/null +++ b/ruoyi-admin/src/main/resources/application-test.yml @@ -0,0 +1,61 @@ +ruoyi: + # 文件路径 + profile: /project/xinchuang/xiaofang-dm/uploadPath + +# 数据源配置 +spring: + datasource: + type: com.alibaba.druid.pool.DruidDataSource + driverClassName: com.mysql.cj.jdbc.Driver + druid: + # 主库数据源 + master: + url: jdbc:mysql://192.168.254.125:3306/xiaofang?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + username: root + password: abcd1234 + # 从库数据源 + slave: + # 从数据源开关/默认关闭 + enabled: false + url: + username: + password: + # 初始连接数 + initialSize: 5 + # 最小连接池数量 + minIdle: 10 + # 最大连接池数量 + maxActive: 20 + # 配置获取连接等待超时的时间 + maxWait: 60000 + # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 + timeBetweenEvictionRunsMillis: 60000 + # 配置一个连接在池中最小生存的时间,单位是毫秒 + minEvictableIdleTimeMillis: 300000 + # 配置一个连接在池中最大生存的时间,单位是毫秒 + maxEvictableIdleTimeMillis: 900000 + # 配置检测连接是否有效 + validationQuery: SELECT 1 FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + webStatFilter: + enabled: true + statViewServlet: + enabled: true + # 设置白名单,不填则允许所有访问 + allow: + url-pattern: /druid/* + # 控制台管理用户名和密码 + login-username: ruoyi + login-password: 123456 + filter: + stat: + enabled: true + # 慢SQL记录 + log-slow-sql: true + slow-sql-millis: 1000 + merge-sql: true + wall: + config: + multi-statement-allow: true diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml new file mode 100644 index 00000000..c05620e3 --- /dev/null +++ b/ruoyi-admin/src/main/resources/application.yml @@ -0,0 +1,154 @@ +# 项目相关配置 +ruoyi: + # 名称 + name: RuoYi + # 版本 + version: 1.0.0 + # 版权年份 + copyrightYear: 2022 + # 实例演示开关 + demoEnabled: true + # 获取ip地址开关 + addressEnabled: false + # 验证码类型 math 数组计算 char 字符验证 + captchaType: math + tenant: + enable: true + ignore-urls: + - /system/tenant/get-id-by-name # 基于名字获取租户,不许带租户编号 + - /captcha/get # 获取图片验证码,和租户无关 + - /captcha/check # 校验图片验证码,和租户无关 + - /infra/file/*/get/** # 获取图片,和租户无关 + - /system/sms/callback/* # 短信回调接口,无法带上租户编号 + - /app-api/pay/order/notify/* # 支付回调通知,不携带租户编号 + - /jmreport/** # 积木报表,无法携带租户编号。*匹配任意字符(除了路径分隔符),而**匹配任意字符 +# 开发环境配置 +server: + # 服务器的HTTP端口,默认为8080 + port: 8080 + servlet: + # 应用的访问路径 + context-path: / + tomcat: + # tomcat的URI编码 + uri-encoding: UTF-8 + # 连接数满后的排队数,默认为100 + accept-count: 1000 + threads: + # tomcat最大线程数,默认为200 + max: 800 + # Tomcat启动初始化的线程数,默认值10 + min-spare: 100 + +# 日志配置 +logging: + level: + com.ruoyi: debug + org.springframework: warn + +# 用户配置 +user: + password: + # 密码最大错误次数 + maxRetryCount: 5 + # 密码锁定时间(默认10分钟) + lockTime: 10 + +# Spring配置 +spring: + # 资源信息 + messages: + # 国际化资源文件路径 + basename: i18n/messages + profiles: + active: dev + # 文件上传 + servlet: + multipart: + # 单个文件大小 + max-file-size: 10MB + # 设置总上传的文件大小 + max-request-size: 20MB + # 服务模块 + devtools: + restart: + # 热部署开关 + enabled: false + cache: + type: ehcache + ehcache: + config: classpath:/ehcache.xml + # redis 配置 +# redis: +# # 地址 +# host: localhost +# # 端口,默认为6379 +# port: 6379 +# # 数据库索引 +# database: 0 +# # 密码 +# password: +# # 连接超时时间 +# timeout: 10s +# lettuce: +# pool: +# # 连接池中的最小空闲连接 +# min-idle: 0 +# # 连接池中的最大空闲连接 +# max-idle: 8 +# # 连接池的最大数据库连接数 +# max-active: 8 +# # #连接池最大阻塞等待时间(使用负值表示没有限制) +# max-wait: -1ms + +# token配置 +token: + # 令牌自定义标识 + header: Authorization + # 令牌密钥 + secret: abcytrmiuumwefsadfdfafahvwxyz + # 令牌有效期(默认30分钟) + expireTime: 30 + +# MyBatis Plus配置 +mybatis-plus: + # 搜索指定包别名 + typeAliasesPackage: com.ruoyi.**.domain + # 配置mapper的扫描,找到所有的mapper.xml映射文件 + mapperLocations: classpath*:mapper/**/*Mapper.xml + # 加载全局的配置文件 + configLocation: classpath:mybatis/mybatis-config.xml + +# PageHelper分页插件 +pagehelper: + helperDialect: mysql + supportMethodsArguments: true + params: count=countSql + +# Swagger配置 +swagger: + # 是否开启swagger + enabled: true + # 请求前缀 + pathMapping: /dev-api + +# 防止XSS攻击 +xss: + # 过滤开关 + enabled: true + # 排除链接(多个用逗号分隔) + excludes: /system/notice + # 匹配链接 + urlPatterns: /system/*,/monitor/*,/tool/* + +# 工作流 Flowable 配置 +flowable: + # 1. false: 默认值,Flowable 启动时,对比数据库表中保存的版本,如果不匹配。将抛出异常 + # 2. true: 启动时会对数据库中所有表进行更新操作,如果表存在,不做处理,反之,自动创建表 + # 3. create_drop: 启动时自动创建表,关闭时自动删除表 + # 4. drop_create: 启动时,删除旧表,再创建新表 + database-schema-update: true # 设置为 false,可通过 https://github.com/flowable/flowable-sql 初始化 + db-history-used: true # flowable6 默认 true 生成信息表,无需手动设置 + check-process-definitions: false # 设置为 false,禁用 /resources/processes 自动部署 BPMN XML 流程 + history-level: full # full:保存历史数据的最高级别,可保存全部流程相关细节,包括流程流转各节点参数 + diff --git a/ruoyi-admin/src/main/resources/banner.txt b/ruoyi-admin/src/main/resources/banner.txt new file mode 100644 index 00000000..0931cb84 --- /dev/null +++ b/ruoyi-admin/src/main/resources/banner.txt @@ -0,0 +1,24 @@ +Application Version: ${ruoyi.version} +Spring Boot Version: ${spring-boot.version} +//////////////////////////////////////////////////////////////////// +// _ooOoo_ // +// o8888888o // +// 88" . "88 // +// (| ^_^ |) // +// O\ = /O // +// ____/`---'\____ // +// .' \\| |// `. // +// / \\||| : |||// \ // +// / _||||| -:- |||||- \ // +// | | \\\ - /// | | // +// | \_| ''\---/'' | | // +// \ .-\__ `-` ___/-. / // +// ___`. .' /--.--\ `. . ___ // +// ."" '< `.___\_<|>_/___.' >'"". // +// | | : `- \`.;`\ _ /`;.`/ - ` : | | // +// \ \ `-. \_ __\ /__ _/ .-` / / // +// ========`-.____`-.___\_____/___.-`____.-'======== // +// `=---=' // +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // +// 佛祖保佑 永不宕机 永无BUG // +//////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/ehcache.xml b/ruoyi-admin/src/main/resources/ehcache.xml new file mode 100644 index 00000000..c2bad019 --- /dev/null +++ b/ruoyi-admin/src/main/resources/ehcache.xml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ruoyi-admin/src/main/resources/i18n/messages.properties b/ruoyi-admin/src/main/resources/i18n/messages.properties new file mode 100644 index 00000000..b0bc5ed0 --- /dev/null +++ b/ruoyi-admin/src/main/resources/i18n/messages.properties @@ -0,0 +1,37 @@ +#错误消息 +not.null=* 必须填写 +user.jcaptcha.error=验证码错误 +user.jcaptcha.expire=验证码已失效 +user.not.exists=用户不存在/密码错误 +user.password.not.match=用户不存在/密码错误 +user.password.retry.limit.count=密码输入错误{0}次 +user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟 +user.password.delete=对不起,您的账号已被删除 +user.blocked=用户已封禁,请联系管理员 +role.blocked=角色已封禁,请联系管理员 +user.logout.success=退出成功 + +length.not.valid=长度必须在{min}到{max}个字符之间 + +user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头 +user.password.not.valid=* 5-50个字符 + +user.email.not.valid=邮箱格式错误 +user.mobile.phone.number.not.valid=手机号格式错误 +user.login.success=登录成功 +user.register.success=注册成功 +user.notfound=请重新登录 +user.forcelogout=管理员强制退出,请重新登录 +user.unknown.error=未知错误,请重新登录 + +##文件上传消息 +upload.exceed.maxSize=上传的文件大小超出限制的文件大小!
允许的文件最大大小是:{0}MB! +upload.filename.exceed.length=上传的文件名最长{0}个字符 + +##权限 +no.permission=您没有数据的权限,请联系管理员添加权限 [{0}] +no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}] +no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}] +no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}] +no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}] +no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}] diff --git a/ruoyi-admin/src/main/resources/logback.xml b/ruoyi-admin/src/main/resources/logback.xml new file mode 100644 index 00000000..9a26a231 --- /dev/null +++ b/ruoyi-admin/src/main/resources/logback.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/sys-info.log + + + + ${log.path}/sys-info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/sys-error.log + + + + ${log.path}/sys-error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + ${log.path}/sys-user.log + + + ${log.path}/sys-user.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/mybatis/mybatis-config.xml b/ruoyi-admin/src/main/resources/mybatis/mybatis-config.xml new file mode 100644 index 00000000..ac47c038 --- /dev/null +++ b/ruoyi-admin/src/main/resources/mybatis/mybatis-config.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + diff --git a/ruoyi-admin/src/main/resources/templatefile/消防安全重点部位情况登记表.xlsx b/ruoyi-admin/src/main/resources/templatefile/消防安全重点部位情况登记表.xlsx new file mode 100644 index 00000000..8e58d705 Binary files /dev/null and b/ruoyi-admin/src/main/resources/templatefile/消防安全重点部位情况登记表.xlsx differ diff --git a/ruoyi-admin/target/classes/META-INF/spring-devtools.properties b/ruoyi-admin/target/classes/META-INF/spring-devtools.properties new file mode 100644 index 00000000..2b23f85a --- /dev/null +++ b/ruoyi-admin/target/classes/META-INF/spring-devtools.properties @@ -0,0 +1 @@ +restart.include.json=/com.alibaba.fastjson.*.jar \ No newline at end of file diff --git a/ruoyi-admin/target/classes/application-dev.yml b/ruoyi-admin/target/classes/application-dev.yml new file mode 100644 index 00000000..0683a1ef --- /dev/null +++ b/ruoyi-admin/target/classes/application-dev.yml @@ -0,0 +1,63 @@ +ruoyi: + # 文件路径 + profile: D:/ruoyi/uploadPath +# 数据源配置 +spring: + datasource: + type: com.alibaba.druid.pool.DruidDataSource + driverClassName: com.mysql.cj.jdbc.Driver + druid: + # 主库数据源 + master: +# url: jdbc:mysql://121.40.191.30:3306/ry-base-tenant?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + url: jdbc:mysql://43.139.7.36:3306/ry-base-tenant?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + username: root + password: it@luokeng +# password: 123456 + + # 从库数据源 + slave: + # 从数据源开关/默认关闭 + enabled: false + url: + username: + password: + # 初始连接数 + initialSize: 5 + # 最小连接池数量 + minIdle: 10 + # 最大连接池数量 + maxActive: 20 + # 配置获取连接等待超时的时间 + maxWait: 60000 + # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 + timeBetweenEvictionRunsMillis: 60000 + # 配置一个连接在池中最小生存的时间,单位是毫秒 + minEvictableIdleTimeMillis: 300000 + # 配置一个连接在池中最大生存的时间,单位是毫秒 + maxEvictableIdleTimeMillis: 900000 + # 配置检测连接是否有效 + validationQuery: SELECT 1 FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + webStatFilter: + enabled: true + statViewServlet: + enabled: true + # 设置白名单,不填则允许所有访问 + allow: + url-pattern: /druid/* + # 控制台管理用户名和密码 + login-username: ruoyi + login-password: 123456 + filter: + stat: + enabled: true + # 慢SQL记录 + log-slow-sql: true + slow-sql-millis: 1000 + merge-sql: true + wall: + config: + multi-statement-allow: true diff --git a/ruoyi-admin/target/classes/application-druid.yml b/ruoyi-admin/target/classes/application-druid.yml new file mode 100644 index 00000000..b4aa52c5 --- /dev/null +++ b/ruoyi-admin/target/classes/application-druid.yml @@ -0,0 +1,57 @@ +# 数据源配置 +spring: + datasource: + type: com.alibaba.druid.pool.DruidDataSource + driverClassName: com.mysql.cj.jdbc.Driver + druid: + # 主库数据源 + master: + url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + username: root + password: root + # 从库数据源 + slave: + # 从数据源开关/默认关闭 + enabled: false + url: + username: + password: + # 初始连接数 + initialSize: 5 + # 最小连接池数量 + minIdle: 10 + # 最大连接池数量 + maxActive: 20 + # 配置获取连接等待超时的时间 + maxWait: 60000 + # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 + timeBetweenEvictionRunsMillis: 60000 + # 配置一个连接在池中最小生存的时间,单位是毫秒 + minEvictableIdleTimeMillis: 300000 + # 配置一个连接在池中最大生存的时间,单位是毫秒 + maxEvictableIdleTimeMillis: 900000 + # 配置检测连接是否有效 + validationQuery: SELECT 1 FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + webStatFilter: + enabled: true + statViewServlet: + enabled: true + # 设置白名单,不填则允许所有访问 + allow: + url-pattern: /druid/* + # 控制台管理用户名和密码 + login-username: ruoyi + login-password: 123456 + filter: + stat: + enabled: true + # 慢SQL记录 + log-slow-sql: true + slow-sql-millis: 1000 + merge-sql: true + wall: + config: + multi-statement-allow: true diff --git a/ruoyi-admin/target/classes/application-test.yml b/ruoyi-admin/target/classes/application-test.yml new file mode 100644 index 00000000..8217380e --- /dev/null +++ b/ruoyi-admin/target/classes/application-test.yml @@ -0,0 +1,61 @@ +ruoyi: + # 文件路径 + profile: /project/xinchuang/xiaofang-dm/uploadPath + +# 数据源配置 +spring: + datasource: + type: com.alibaba.druid.pool.DruidDataSource + driverClassName: com.mysql.cj.jdbc.Driver + druid: + # 主库数据源 + master: + url: jdbc:mysql://192.168.254.125:3306/xiaofang?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + username: root + password: abcd1234 + # 从库数据源 + slave: + # 从数据源开关/默认关闭 + enabled: false + url: + username: + password: + # 初始连接数 + initialSize: 5 + # 最小连接池数量 + minIdle: 10 + # 最大连接池数量 + maxActive: 20 + # 配置获取连接等待超时的时间 + maxWait: 60000 + # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 + timeBetweenEvictionRunsMillis: 60000 + # 配置一个连接在池中最小生存的时间,单位是毫秒 + minEvictableIdleTimeMillis: 300000 + # 配置一个连接在池中最大生存的时间,单位是毫秒 + maxEvictableIdleTimeMillis: 900000 + # 配置检测连接是否有效 + validationQuery: SELECT 1 FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + webStatFilter: + enabled: true + statViewServlet: + enabled: true + # 设置白名单,不填则允许所有访问 + allow: + url-pattern: /druid/* + # 控制台管理用户名和密码 + login-username: ruoyi + login-password: 123456 + filter: + stat: + enabled: true + # 慢SQL记录 + log-slow-sql: true + slow-sql-millis: 1000 + merge-sql: true + wall: + config: + multi-statement-allow: true diff --git a/ruoyi-admin/target/classes/application.yml b/ruoyi-admin/target/classes/application.yml new file mode 100644 index 00000000..c05620e3 --- /dev/null +++ b/ruoyi-admin/target/classes/application.yml @@ -0,0 +1,154 @@ +# 项目相关配置 +ruoyi: + # 名称 + name: RuoYi + # 版本 + version: 1.0.0 + # 版权年份 + copyrightYear: 2022 + # 实例演示开关 + demoEnabled: true + # 获取ip地址开关 + addressEnabled: false + # 验证码类型 math 数组计算 char 字符验证 + captchaType: math + tenant: + enable: true + ignore-urls: + - /system/tenant/get-id-by-name # 基于名字获取租户,不许带租户编号 + - /captcha/get # 获取图片验证码,和租户无关 + - /captcha/check # 校验图片验证码,和租户无关 + - /infra/file/*/get/** # 获取图片,和租户无关 + - /system/sms/callback/* # 短信回调接口,无法带上租户编号 + - /app-api/pay/order/notify/* # 支付回调通知,不携带租户编号 + - /jmreport/** # 积木报表,无法携带租户编号。*匹配任意字符(除了路径分隔符),而**匹配任意字符 +# 开发环境配置 +server: + # 服务器的HTTP端口,默认为8080 + port: 8080 + servlet: + # 应用的访问路径 + context-path: / + tomcat: + # tomcat的URI编码 + uri-encoding: UTF-8 + # 连接数满后的排队数,默认为100 + accept-count: 1000 + threads: + # tomcat最大线程数,默认为200 + max: 800 + # Tomcat启动初始化的线程数,默认值10 + min-spare: 100 + +# 日志配置 +logging: + level: + com.ruoyi: debug + org.springframework: warn + +# 用户配置 +user: + password: + # 密码最大错误次数 + maxRetryCount: 5 + # 密码锁定时间(默认10分钟) + lockTime: 10 + +# Spring配置 +spring: + # 资源信息 + messages: + # 国际化资源文件路径 + basename: i18n/messages + profiles: + active: dev + # 文件上传 + servlet: + multipart: + # 单个文件大小 + max-file-size: 10MB + # 设置总上传的文件大小 + max-request-size: 20MB + # 服务模块 + devtools: + restart: + # 热部署开关 + enabled: false + cache: + type: ehcache + ehcache: + config: classpath:/ehcache.xml + # redis 配置 +# redis: +# # 地址 +# host: localhost +# # 端口,默认为6379 +# port: 6379 +# # 数据库索引 +# database: 0 +# # 密码 +# password: +# # 连接超时时间 +# timeout: 10s +# lettuce: +# pool: +# # 连接池中的最小空闲连接 +# min-idle: 0 +# # 连接池中的最大空闲连接 +# max-idle: 8 +# # 连接池的最大数据库连接数 +# max-active: 8 +# # #连接池最大阻塞等待时间(使用负值表示没有限制) +# max-wait: -1ms + +# token配置 +token: + # 令牌自定义标识 + header: Authorization + # 令牌密钥 + secret: abcytrmiuumwefsadfdfafahvwxyz + # 令牌有效期(默认30分钟) + expireTime: 30 + +# MyBatis Plus配置 +mybatis-plus: + # 搜索指定包别名 + typeAliasesPackage: com.ruoyi.**.domain + # 配置mapper的扫描,找到所有的mapper.xml映射文件 + mapperLocations: classpath*:mapper/**/*Mapper.xml + # 加载全局的配置文件 + configLocation: classpath:mybatis/mybatis-config.xml + +# PageHelper分页插件 +pagehelper: + helperDialect: mysql + supportMethodsArguments: true + params: count=countSql + +# Swagger配置 +swagger: + # 是否开启swagger + enabled: true + # 请求前缀 + pathMapping: /dev-api + +# 防止XSS攻击 +xss: + # 过滤开关 + enabled: true + # 排除链接(多个用逗号分隔) + excludes: /system/notice + # 匹配链接 + urlPatterns: /system/*,/monitor/*,/tool/* + +# 工作流 Flowable 配置 +flowable: + # 1. false: 默认值,Flowable 启动时,对比数据库表中保存的版本,如果不匹配。将抛出异常 + # 2. true: 启动时会对数据库中所有表进行更新操作,如果表存在,不做处理,反之,自动创建表 + # 3. create_drop: 启动时自动创建表,关闭时自动删除表 + # 4. drop_create: 启动时,删除旧表,再创建新表 + database-schema-update: true # 设置为 false,可通过 https://github.com/flowable/flowable-sql 初始化 + db-history-used: true # flowable6 默认 true 生成信息表,无需手动设置 + check-process-definitions: false # 设置为 false,禁用 /resources/processes 自动部署 BPMN XML 流程 + history-level: full # full:保存历史数据的最高级别,可保存全部流程相关细节,包括流程流转各节点参数 + diff --git a/ruoyi-admin/target/classes/banner.txt b/ruoyi-admin/target/classes/banner.txt new file mode 100644 index 00000000..0931cb84 --- /dev/null +++ b/ruoyi-admin/target/classes/banner.txt @@ -0,0 +1,24 @@ +Application Version: ${ruoyi.version} +Spring Boot Version: ${spring-boot.version} +//////////////////////////////////////////////////////////////////// +// _ooOoo_ // +// o8888888o // +// 88" . "88 // +// (| ^_^ |) // +// O\ = /O // +// ____/`---'\____ // +// .' \\| |// `. // +// / \\||| : |||// \ // +// / _||||| -:- |||||- \ // +// | | \\\ - /// | | // +// | \_| ''\---/'' | | // +// \ .-\__ `-` ___/-. / // +// ___`. .' /--.--\ `. . ___ // +// ."" '< `.___\_<|>_/___.' >'"". // +// | | : `- \`.;`\ _ /`;.`/ - ` : | | // +// \ \ `-. \_ __\ /__ _/ .-` / / // +// ========`-.____`-.___\_____/___.-`____.-'======== // +// `=---=' // +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // +// 佛祖保佑 永不宕机 永无BUG // +//////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/ruoyi-admin/target/classes/com/ruoyi/RuoYiApplication.class b/ruoyi-admin/target/classes/com/ruoyi/RuoYiApplication.class new file mode 100644 index 00000000..622bd74c Binary files /dev/null and b/ruoyi-admin/target/classes/com/ruoyi/RuoYiApplication.class differ diff --git a/ruoyi-admin/target/classes/com/ruoyi/RuoYiServletInitializer.class b/ruoyi-admin/target/classes/com/ruoyi/RuoYiServletInitializer.class new file mode 100644 index 00000000..57c45ef9 Binary files /dev/null and b/ruoyi-admin/target/classes/com/ruoyi/RuoYiServletInitializer.class differ diff --git a/ruoyi-admin/target/classes/com/ruoyi/TestFlowableApplication.class b/ruoyi-admin/target/classes/com/ruoyi/TestFlowableApplication.class new file mode 100644 index 00000000..b756b93e Binary files /dev/null and b/ruoyi-admin/target/classes/com/ruoyi/TestFlowableApplication.class differ diff --git a/ruoyi-admin/target/classes/com/ruoyi/web/DevinUtil.class b/ruoyi-admin/target/classes/com/ruoyi/web/DevinUtil.class new file mode 100644 index 00000000..4a06690e Binary files /dev/null and b/ruoyi-admin/target/classes/com/ruoyi/web/DevinUtil.class differ diff --git a/ruoyi-admin/target/classes/com/ruoyi/web/controller/common/CaptchaController.class b/ruoyi-admin/target/classes/com/ruoyi/web/controller/common/CaptchaController.class new file mode 100644 index 00000000..e250ac12 Binary files /dev/null and b/ruoyi-admin/target/classes/com/ruoyi/web/controller/common/CaptchaController.class differ diff --git a/ruoyi-admin/target/classes/com/ruoyi/web/controller/common/CommonController.class b/ruoyi-admin/target/classes/com/ruoyi/web/controller/common/CommonController.class new file mode 100644 index 00000000..a47791c1 Binary files /dev/null and b/ruoyi-admin/target/classes/com/ruoyi/web/controller/common/CommonController.class differ diff --git a/ruoyi-admin/target/classes/com/ruoyi/web/controller/monitor/ServerController.class b/ruoyi-admin/target/classes/com/ruoyi/web/controller/monitor/ServerController.class new file mode 100644 index 00000000..54c33012 Binary files /dev/null and b/ruoyi-admin/target/classes/com/ruoyi/web/controller/monitor/ServerController.class differ diff --git a/ruoyi-admin/target/classes/com/ruoyi/web/controller/monitor/SysLogininforController.class b/ruoyi-admin/target/classes/com/ruoyi/web/controller/monitor/SysLogininforController.class new file mode 100644 index 00000000..3a42803c Binary files /dev/null and b/ruoyi-admin/target/classes/com/ruoyi/web/controller/monitor/SysLogininforController.class differ diff --git a/ruoyi-admin/target/classes/com/ruoyi/web/controller/monitor/SysOperlogController.class b/ruoyi-admin/target/classes/com/ruoyi/web/controller/monitor/SysOperlogController.class new file mode 100644 index 00000000..3e08e462 Binary files /dev/null and b/ruoyi-admin/target/classes/com/ruoyi/web/controller/monitor/SysOperlogController.class differ diff --git a/ruoyi-admin/target/classes/com/ruoyi/web/controller/monitor/SysUserOnlineController.class b/ruoyi-admin/target/classes/com/ruoyi/web/controller/monitor/SysUserOnlineController.class new file mode 100644 index 00000000..81014f7b Binary files /dev/null and b/ruoyi-admin/target/classes/com/ruoyi/web/controller/monitor/SysUserOnlineController.class differ diff --git a/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysConfigController.class b/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysConfigController.class new file mode 100644 index 00000000..e33e637e Binary files /dev/null and b/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysConfigController.class differ diff --git a/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysDeptController.class b/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysDeptController.class new file mode 100644 index 00000000..9dde9809 Binary files /dev/null and b/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysDeptController.class differ diff --git a/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysDictDataController.class b/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysDictDataController.class new file mode 100644 index 00000000..20df5911 Binary files /dev/null and b/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysDictDataController.class differ diff --git a/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysDictTypeController.class b/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysDictTypeController.class new file mode 100644 index 00000000..6c2c3dbf Binary files /dev/null and b/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysDictTypeController.class differ diff --git a/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysIndexController.class b/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysIndexController.class new file mode 100644 index 00000000..4e6fdb06 Binary files /dev/null and b/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysIndexController.class differ diff --git a/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysLoginController.class b/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysLoginController.class new file mode 100644 index 00000000..afa71048 Binary files /dev/null and b/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysLoginController.class differ diff --git a/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysMenuController.class b/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysMenuController.class new file mode 100644 index 00000000..146c36f9 Binary files /dev/null and b/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysMenuController.class differ diff --git a/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysNoticeController.class b/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysNoticeController.class new file mode 100644 index 00000000..7e313568 Binary files /dev/null and b/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysNoticeController.class differ diff --git a/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysPostController.class b/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysPostController.class new file mode 100644 index 00000000..f4e3641e Binary files /dev/null and b/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysPostController.class differ diff --git a/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysProfileController.class b/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysProfileController.class new file mode 100644 index 00000000..9e6981d7 Binary files /dev/null and b/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysProfileController.class differ diff --git a/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysRegisterController.class b/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysRegisterController.class new file mode 100644 index 00000000..0fc9d05c Binary files /dev/null and b/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysRegisterController.class differ diff --git a/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysRoleController.class b/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysRoleController.class new file mode 100644 index 00000000..6c1d0503 Binary files /dev/null and b/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysRoleController.class differ diff --git a/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysTreeDictController.class b/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysTreeDictController.class new file mode 100644 index 00000000..5ce95268 Binary files /dev/null and b/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysTreeDictController.class differ diff --git a/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysTreeDictDataController.class b/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysTreeDictDataController.class new file mode 100644 index 00000000..480aaec0 Binary files /dev/null and b/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysTreeDictDataController.class differ diff --git a/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysUserController.class b/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysUserController.class new file mode 100644 index 00000000..246ba21d Binary files /dev/null and b/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/SysUserController.class differ diff --git a/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/TenantController.class b/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/TenantController.class new file mode 100644 index 00000000..91628cfc Binary files /dev/null and b/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/TenantController.class differ diff --git a/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/TenantPackageController.class b/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/TenantPackageController.class new file mode 100644 index 00000000..48214ece Binary files /dev/null and b/ruoyi-admin/target/classes/com/ruoyi/web/controller/system/TenantPackageController.class differ diff --git a/ruoyi-admin/target/classes/com/ruoyi/web/controller/tool/SwaggerController.class b/ruoyi-admin/target/classes/com/ruoyi/web/controller/tool/SwaggerController.class new file mode 100644 index 00000000..57181b86 Binary files /dev/null and b/ruoyi-admin/target/classes/com/ruoyi/web/controller/tool/SwaggerController.class differ diff --git a/ruoyi-admin/target/classes/com/ruoyi/web/controller/tool/TestController.class b/ruoyi-admin/target/classes/com/ruoyi/web/controller/tool/TestController.class new file mode 100644 index 00000000..e1ba511e Binary files /dev/null and b/ruoyi-admin/target/classes/com/ruoyi/web/controller/tool/TestController.class differ diff --git a/ruoyi-admin/target/classes/com/ruoyi/web/controller/tool/UserEntity.class b/ruoyi-admin/target/classes/com/ruoyi/web/controller/tool/UserEntity.class new file mode 100644 index 00000000..dec262a7 Binary files /dev/null and b/ruoyi-admin/target/classes/com/ruoyi/web/controller/tool/UserEntity.class differ diff --git a/ruoyi-admin/target/classes/com/ruoyi/web/core/config/SwaggerConfig.class b/ruoyi-admin/target/classes/com/ruoyi/web/core/config/SwaggerConfig.class new file mode 100644 index 00000000..39c4b3f5 Binary files /dev/null and b/ruoyi-admin/target/classes/com/ruoyi/web/core/config/SwaggerConfig.class differ diff --git a/ruoyi-admin/target/classes/ehcache.xml b/ruoyi-admin/target/classes/ehcache.xml new file mode 100644 index 00000000..c2bad019 --- /dev/null +++ b/ruoyi-admin/target/classes/ehcache.xml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ruoyi-admin/target/classes/i18n/messages.properties b/ruoyi-admin/target/classes/i18n/messages.properties new file mode 100644 index 00000000..b0bc5ed0 --- /dev/null +++ b/ruoyi-admin/target/classes/i18n/messages.properties @@ -0,0 +1,37 @@ +#错误消息 +not.null=* 必须填写 +user.jcaptcha.error=验证码错误 +user.jcaptcha.expire=验证码已失效 +user.not.exists=用户不存在/密码错误 +user.password.not.match=用户不存在/密码错误 +user.password.retry.limit.count=密码输入错误{0}次 +user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟 +user.password.delete=对不起,您的账号已被删除 +user.blocked=用户已封禁,请联系管理员 +role.blocked=角色已封禁,请联系管理员 +user.logout.success=退出成功 + +length.not.valid=长度必须在{min}到{max}个字符之间 + +user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头 +user.password.not.valid=* 5-50个字符 + +user.email.not.valid=邮箱格式错误 +user.mobile.phone.number.not.valid=手机号格式错误 +user.login.success=登录成功 +user.register.success=注册成功 +user.notfound=请重新登录 +user.forcelogout=管理员强制退出,请重新登录 +user.unknown.error=未知错误,请重新登录 + +##文件上传消息 +upload.exceed.maxSize=上传的文件大小超出限制的文件大小!
允许的文件最大大小是:{0}MB! +upload.filename.exceed.length=上传的文件名最长{0}个字符 + +##权限 +no.permission=您没有数据的权限,请联系管理员添加权限 [{0}] +no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}] +no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}] +no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}] +no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}] +no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}] diff --git a/ruoyi-admin/target/classes/logback.xml b/ruoyi-admin/target/classes/logback.xml new file mode 100644 index 00000000..9a26a231 --- /dev/null +++ b/ruoyi-admin/target/classes/logback.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/sys-info.log + + + + ${log.path}/sys-info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/sys-error.log + + + + ${log.path}/sys-error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + ${log.path}/sys-user.log + + + ${log.path}/sys-user.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ruoyi-admin/target/classes/mybatis/mybatis-config.xml b/ruoyi-admin/target/classes/mybatis/mybatis-config.xml new file mode 100644 index 00000000..ac47c038 --- /dev/null +++ b/ruoyi-admin/target/classes/mybatis/mybatis-config.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + diff --git a/ruoyi-admin/target/classes/templatefile/消防安全重点部位情况登记表.xlsx b/ruoyi-admin/target/classes/templatefile/消防安全重点部位情况登记表.xlsx new file mode 100644 index 00000000..8e58d705 Binary files /dev/null and b/ruoyi-admin/target/classes/templatefile/消防安全重点部位情况登记表.xlsx differ diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml new file mode 100644 index 00000000..b929a62b --- /dev/null +++ b/ruoyi-common/pom.xml @@ -0,0 +1,206 @@ + + + + ruoyi + com.ruoyi + 3.8.4 + + 4.0.0 + + ruoyi-common + + + common通用工具 + + + + + com.alibaba + easyexcel + + + + org.springframework + spring-context-support + + + + + org.springframework + spring-web + + + + + org.springframework.boot + spring-boot-starter-security + + + + + com.github.pagehelper + pagehelper-spring-boot-starter + + + org.mybatis + mybatis + + + org.mybatis + mybatis-spring + + + com.github.jsqlparser + jsqlparser + + + + + + + org.springframework.boot + spring-boot-starter-validation + + + + + org.apache.commons + commons-lang3 + + + + + com.fasterxml.jackson.core + jackson-databind + + + + + com.alibaba.fastjson2 + fastjson2 + + + + + commons-io + commons-io + + + + + commons-fileupload + commons-fileupload + + + + + org.apache.poi + poi-ooxml + + + + + org.yaml + snakeyaml + + + + + io.jsonwebtoken + jjwt + + + + + javax.xml.bind + jaxb-api + + + + net.sf.ehcache + ehcache + 2.10.6 + + + org.springframework.boot + spring-boot-starter-cache + + + + + + + + + + + org.apache.commons + commons-pool2 + + + + + eu.bitwalker + UserAgentUtils + + + + + javax.servlet + javax.servlet-api + + + + + cn.hutool + hutool-all + + + + + com.baomidou + mybatis-plus-boot-starter + + + + + org.projectlombok + lombok + true + + + + + com.github.xiaoymin + knife4j-spring-boot-starter + + + + + com.alibaba + druid-spring-boot-starter + + + + org.mapstruct + mapstruct + + + org.mapstruct + mapstruct-jdk8 + + + org.mapstruct + mapstruct-processor + + + + com.alibaba + transmittable-thread-local + + + + + diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Anonymous.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Anonymous.java new file mode 100644 index 00000000..1d6d4f44 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Anonymous.java @@ -0,0 +1,19 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 匿名访问不鉴权注解 + * + * @author ruoyi + */ +@Target({ ElementType.METHOD, ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Anonymous +{ +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java new file mode 100644 index 00000000..aae7f72c --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java @@ -0,0 +1,33 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 数据权限过滤注解 + * + * @author ruoyi + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface DataScope +{ + /** + * 部门表的别名 + */ + public String deptAlias() default ""; + + /** + * 用户表的别名 + */ + public String userAlias() default ""; + + /** + * 权限字符(用于多个角色匹配符合要求的权限)默认根据权限注解@ss获取,多个权限用逗号分隔开来 + */ + public String permission() default ""; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSource.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSource.java new file mode 100644 index 00000000..79cd191f --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSource.java @@ -0,0 +1,28 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import com.ruoyi.common.enums.DataSourceType; + +/** + * 自定义多数据源切换注解 + * + * 优先级:先方法,后类,如果方法覆盖了类上的数据源类型,以方法的为准,否则以类上的为准 + * + * @author ruoyi + */ +@Target({ ElementType.METHOD, ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +public @interface DataSource +{ + /** + * 切换数据源名称 + */ + public DataSourceType value() default DataSourceType.MASTER; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DictFormat.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DictFormat.java new file mode 100644 index 00000000..5ff76a07 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DictFormat.java @@ -0,0 +1,22 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.*; + +/** + * 字典格式化 + * + * 实现将字典数据的值,格式化成字典数据的标签 + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface DictFormat { + + /** + * 例如说,SysDictTypeConstants、InfDictTypeConstants + * + * @return 字典类型 + */ + String value(); + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java new file mode 100644 index 00000000..a8fa282b --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java @@ -0,0 +1,187 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.math.BigDecimal; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.ss.usermodel.IndexedColors; +import com.ruoyi.common.utils.poi.ExcelHandlerAdapter; + +/** + * 自定义导出Excel数据注解 + * + * @author ruoyi + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface Excel +{ + /** + * 导出时在excel中排序 + */ + public int sort() default Integer.MAX_VALUE; + + /** + * 导出到Excel中的名字. + */ + public String name() default ""; + + /** + * 日期格式, 如: yyyy-MM-dd + */ + public String dateFormat() default ""; + + /** + * 如果是字典类型,请设置字典的type值 (如: sys_user_sex) + */ + public String dictType() default ""; + + /** + * 读取内容转表达式 (如: 0=男,1=女,2=未知) + */ + public String readConverterExp() default ""; + + /** + * 分隔符,读取字符串组内容 + */ + public String separator() default ","; + + /** + * BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化) + */ + public int scale() default -1; + + /** + * BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN + */ + public int roundingMode() default BigDecimal.ROUND_HALF_EVEN; + + /** + * 导出时在excel中每个列的高度 单位为字符 + */ + public double height() default 14; + + /** + * 导出时在excel中每个列的宽 单位为字符 + */ + public double width() default 16; + + /** + * 文字后缀,如% 90 变成90% + */ + public String suffix() default ""; + + /** + * 当值为空时,字段的默认值 + */ + public String defaultValue() default ""; + + /** + * 提示信息 + */ + public String prompt() default ""; + + /** + * 设置只能选择不能输入的列内容. + */ + public String[] combo() default {}; + + /** + * 是否需要纵向合并单元格,应对需求:含有list集合单元格) + */ + public boolean needMerge() default false; + + /** + * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写. + */ + public boolean isExport() default true; + + /** + * 另一个类中的属性名称,支持多级获取,以小数点隔开 + */ + public String targetAttr() default ""; + + /** + * 是否自动统计数据,在最后追加一行统计数据总和 + */ + public boolean isStatistics() default false; + + /** + * 导出类型(0数字 1字符串 2图片) + */ + public ColumnType cellType() default ColumnType.STRING; + + /** + * 导出列头背景色 + */ + public IndexedColors headerBackgroundColor() default IndexedColors.GREY_50_PERCENT; + + /** + * 导出列头字体颜色 + */ + public IndexedColors headerColor() default IndexedColors.WHITE; + + /** + * 导出单元格背景色 + */ + public IndexedColors backgroundColor() default IndexedColors.WHITE; + + /** + * 导出单元格字体颜色 + */ + public IndexedColors color() default IndexedColors.BLACK; + + /** + * 导出字段对齐方式 + */ + public HorizontalAlignment align() default HorizontalAlignment.CENTER; + + /** + * 自定义数据处理器 + */ + public Class handler() default ExcelHandlerAdapter.class; + + /** + * 自定义数据处理器参数 + */ + public String[] args() default {}; + + /** + * 字段类型(0:导出导入;1:仅导出;2:仅导入) + */ + Type type() default Type.ALL; + + public enum Type + { + ALL(0), EXPORT(1), IMPORT(2); + private final int value; + + Type(int value) + { + this.value = value; + } + + public int value() + { + return this.value; + } + } + + public enum ColumnType + { + NUMERIC(0), STRING(1), IMAGE(2); + private final int value; + + ColumnType(int value) + { + this.value = value; + } + + public int value() + { + return this.value; + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excels.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excels.java new file mode 100644 index 00000000..1f1cc81b --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excels.java @@ -0,0 +1,18 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Excel注解集 + * + * @author ruoyi + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Excels +{ + public Excel[] value(); +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Log.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Log.java new file mode 100644 index 00000000..ca02c6c4 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Log.java @@ -0,0 +1,46 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.enums.OperatorType; + +/** + * 自定义操作日志记录注解 + * + * @author ruoyi + * + */ +@Target({ ElementType.PARAMETER, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Log +{ + /** + * 模块 + */ + public String title() default ""; + + /** + * 功能 + */ + public BusinessType businessType() default BusinessType.OTHER; + + /** + * 操作人类别 + */ + public OperatorType operatorType() default OperatorType.MANAGE; + + /** + * 是否保存请求的参数 + */ + public boolean isSaveRequestData() default true; + + /** + * 是否保存响应的参数 + */ + public boolean isSaveResponseData() default true; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RateLimiter.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RateLimiter.java new file mode 100644 index 00000000..0f024c7d --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RateLimiter.java @@ -0,0 +1,40 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.enums.LimitType; + +/** + * 限流注解 + * + * @author ruoyi + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RateLimiter +{ + /** + * 限流key + */ + public String key() default CacheConstants.RATE_LIMIT_KEY; + + /** + * 限流时间,单位秒 + */ + public int time() default 60; + + /** + * 限流次数 + */ + public int count() default 100; + + /** + * 限流类型 + */ + public LimitType limitType() default LimitType.DEFAULT; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java new file mode 100644 index 00000000..b7697481 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java @@ -0,0 +1,31 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自定义注解防止表单重复提交 + * + * @author ruoyi + * + */ +@Inherited +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RepeatSubmit +{ + /** + * 间隔时间(ms),小于此时间视为重复提交 + */ + public int interval() default 5000; + + /** + * 提示消息 + */ + public String message() default "不允许重复提交,请稍候再试"; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/ReportExcel.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/ReportExcel.java new file mode 100644 index 00000000..f1e13972 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/ReportExcel.java @@ -0,0 +1,26 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自定义easy excel报表下载表单时对数据进行格式转换 + * + * @author ruoyi + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface ReportExcel +{ + + /** + * 如果是字典类型,请设置字典的type值 (如: sys_user_sex) + */ + public String dictType() default ""; + /** + * 如果是字典类型,值的转方式。0-为展示所有字典值并且带□和☑ + */ + public String dictTransferType() default "0"; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java b/ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java new file mode 100644 index 00000000..9f69c358 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java @@ -0,0 +1,150 @@ +package com.ruoyi.common.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * 读取项目相关配置 + * + * @author ruoyi + */ +@Component +@ConfigurationProperties(prefix = "ruoyi") +public class RuoYiConfig +{ + /** 项目名称 */ + private String name; + + /** 版本 */ + private String version; + + /** 版权年份 */ + private String copyrightYear; + + /** 实例演示开关 */ + private boolean demoEnabled; + + /** 上传路径 */ + private static String profile; + + /** 获取地址开关 */ + private static boolean addressEnabled; + + /** 验证码类型 */ + private static String captchaType; + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public String getVersion() + { + return version; + } + + public void setVersion(String version) + { + this.version = version; + } + + public String getCopyrightYear() + { + return copyrightYear; + } + + public void setCopyrightYear(String copyrightYear) + { + this.copyrightYear = copyrightYear; + } + + public boolean isDemoEnabled() + { + return demoEnabled; + } + + public void setDemoEnabled(boolean demoEnabled) + { + this.demoEnabled = demoEnabled; + } + + public static String getProfile() + { + return profile; + } + + public void setProfile(String profile) + { + RuoYiConfig.profile = profile; + } + + public static boolean isAddressEnabled() + { + return addressEnabled; + } + + public void setAddressEnabled(boolean addressEnabled) + { + RuoYiConfig.addressEnabled = addressEnabled; + } + + public static String getCaptchaType() { + return captchaType; + } + + public void setCaptchaType(String captchaType) { + RuoYiConfig.captchaType = captchaType; + } + + /** + * 获取导入上传路径 + */ + public static String getImportPath() + { + return getProfile() + "/import"; + } + + /** + * 获取头像上传路径 + */ + public static String getAvatarPath() + { + return getProfile() + "/avatar"; + } + + /** + * 获取下载路径 + */ + public static String getDownloadPath() + { + return getProfile() + "/download/"; + } + /** + * 获取临时下载路径,改文件夹下的文件会被定时删除 + */ + public static String getTempDownloadPath() + { + return getProfile() + "/download/temp/"; + } + /** + * 获取上传路径 + */ + public static String getUploadPath() + { + return getProfile() + "/upload"; + } + + /** + * 获取文件模板路径 + */ + public static String getTemplateFilePath() + { + return getProfile() + "/template"; + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java new file mode 100644 index 00000000..296733b9 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java @@ -0,0 +1,54 @@ +package com.ruoyi.common.constant; + +/** + * 缓存的key 常量 + * + * @author ruoyi + */ +public class CacheConstants +{ + /** + * 登录用户 redis key + */ + public static final String LOGIN_TOKEN_KEY = "login_tokens:"; + + /** + * 验证码 redis key + */ + public static final String CAPTCHA_CODE_KEY = "captcha_codes:"; + + /** + * 参数管理 cache key + */ + public static final String SYS_CONFIG_KEY = "sys_config:"; + + /** + * 字典管理 cache key + */ + public static final String SYS_DICT_KEY = "sys_dict:"; + + /** + * 分类管理-list cache key + */ + public static final String TREE_DICT_KEY = "sys_tree_dict:"; + + /** + * 防重提交 redis key + */ + public static final String REPEAT_SUBMIT_KEY = "repeat_submit:"; + + /** + * 限流 redis key + */ + public static final String RATE_LIMIT_KEY = "rate_limit:"; + + /** + * 登录账户密码错误次数 redis key + */ + public static final String PWD_ERR_CNT_KEY = "pwd_err_cnt:"; + + /** + * 租户管理 cache key + */ + public static final String SYS_TENANT_KEY = "sys_tenant:"; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java new file mode 100644 index 00000000..ed687257 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java @@ -0,0 +1,152 @@ +package com.ruoyi.common.constant; + +import io.jsonwebtoken.Claims; + +/** + * 通用常量信息 + * + * @author ruoyi + */ +public class Constants +{ + /** + * UTF-8 字符集 + */ + public static final String UTF8 = "UTF-8"; + + /** + * GBK 字符集 + */ + public static final String GBK = "GBK"; + + /** + * www主域 + */ + public static final String WWW = "www."; + + /** + * http请求 + */ + public static final String HTTP = "http://"; + + /** + * https请求 + */ + public static final String HTTPS = "https://"; + + /** + * 通用成功标识 + */ + public static final String SUCCESS = "0"; + + /** + * 通用失败标识 + */ + public static final String FAIL = "1"; + + /** + * 通用true标识 + */ + public static final String TRUE = "1"; + + /** + * 通用false标识 + */ + public static final String FALSE = "0"; + + /** + * 登录成功 + */ + public static final String LOGIN_SUCCESS = "Success"; + + /** + * 注销 + */ + public static final String LOGOUT = "Logout"; + + /** + * 注册 + */ + public static final String REGISTER = "Register"; + + /** + * 登录失败 + */ + public static final String LOGIN_FAIL = "Error"; + + /** + * 验证码有效期(分钟) + */ + public static final Integer CAPTCHA_EXPIRATION = 2; + + /** + * 令牌 + */ + public static final String TOKEN = "token"; + + /** + * 令牌前缀 + */ + public static final String TOKEN_PREFIX = "Bearer "; + + /** + * 令牌前缀 + */ + public static final String LOGIN_USER_KEY = "login_user_key"; + + /** + * 用户ID + */ + public static final String JWT_USERID = "userid"; + + /** + * 用户名称 + */ + public static final String JWT_USERNAME = Claims.SUBJECT; + + /** + * 用户头像 + */ + public static final String JWT_AVATAR = "avatar"; + + /** + * 创建时间 + */ + public static final String JWT_CREATED = "created"; + + /** + * 用户权限 + */ + public static final String JWT_AUTHORITIES = "authorities"; + + /** + * 资源映射路径 前缀 + */ + public static final String RESOURCE_PREFIX = "/profile"; + + /** + * RMI 远程方法调用 + */ + public static final String LOOKUP_RMI = "rmi:"; + + /** + * LDAP 远程方法调用 + */ + public static final String LOOKUP_LDAP = "ldap:"; + + /** + * LDAPS 远程方法调用 + */ + public static final String LOOKUP_LDAPS = "ldaps:"; + + /** + * 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加) + */ + public static final String[] JOB_WHITELIST_STR = { "com.ruoyi" }; + + /** + * 定时任务违规的字符 + */ + public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml", + "org.springframework", "org.apache", "com.ruoyi.common.utils.file" }; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/ErrorCodeConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/ErrorCodeConstants.java new file mode 100644 index 00000000..66485bea --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/ErrorCodeConstants.java @@ -0,0 +1,145 @@ +package com.ruoyi.common.constant; + + +import com.ruoyi.common.exception.ErrorCode; + +/** + * Member 错误码枚举类 + *

+ * member 系统,使用 1-004-000-000 段 + */ +public interface ErrorCodeConstants { + + // ========== AUTH 模块 1002000000 ========== + ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1002000000, "登录失败,账号密码不正确"); + ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1002000001, "登录失败,账号被禁用"); + ErrorCode AUTH_LOGIN_CAPTCHA_CODE_ERROR = new ErrorCode(1002000004, "验证码不正确,原因:{}"); + ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1002000005, "未绑定账号,需要进行绑定"); + ErrorCode AUTH_TOKEN_EXPIRED = new ErrorCode(1002000006, "Token 已经过期"); + ErrorCode AUTH_MOBILE_NOT_EXISTS = new ErrorCode(1002000007, "手机号不存在"); + + // ========== 菜单模块 1002001000 ========== + ErrorCode MENU_NAME_DUPLICATE = new ErrorCode(1002001000, "已经存在该名字的菜单"); + ErrorCode MENU_PARENT_NOT_EXISTS = new ErrorCode(1002001001, "父菜单不存在"); + ErrorCode MENU_PARENT_ERROR = new ErrorCode(1002001002, "不能设置自己为父菜单"); + ErrorCode MENU_NOT_EXISTS = new ErrorCode(1002001003, "菜单不存在"); + ErrorCode MENU_EXISTS_CHILDREN = new ErrorCode(1002001004, "存在子菜单,无法删除"); + ErrorCode MENU_PARENT_NOT_DIR_OR_MENU = new ErrorCode(1002001005, "父菜单的类型必须是目录或者菜单"); + + // ========== 角色模块 1002002000 ========== + ErrorCode ROLE_NOT_EXISTS = new ErrorCode(1002002000, "角色不存在"); + ErrorCode ROLE_NAME_DUPLICATE = new ErrorCode(1002002001, "已经存在名为【{}】的角色"); + ErrorCode ROLE_CODE_DUPLICATE = new ErrorCode(1002002002, "已经存在编码为【{}】的角色"); + ErrorCode ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE = new ErrorCode(1002002003, "不能操作类型为系统内置的角色"); + ErrorCode ROLE_IS_DISABLE = new ErrorCode(1002002004, "名字为【{}】的角色已被禁用"); + ErrorCode ROLE_ADMIN_CODE_ERROR = new ErrorCode(1002002005, "编码【{}】不能使用"); + + // ========== 用户模块 1002003000 ========== + ErrorCode USER_USERNAME_EXISTS = new ErrorCode(1002003000, "用户账号已经存在"); + ErrorCode USER_MOBILE_EXISTS = new ErrorCode(1002003001, "手机号已经存在"); + ErrorCode USER_EMAIL_EXISTS = new ErrorCode(1002003002, "邮箱已经存在"); + ErrorCode USER_NOT_EXISTS = new ErrorCode(1002003003, "用户不存在"); + ErrorCode USER_IMPORT_LIST_IS_EMPTY = new ErrorCode(1002003004, "导入用户数据不能为空!"); + ErrorCode USER_PASSWORD_FAILED = new ErrorCode(1002003005, "用户密码校验失败"); + ErrorCode USER_IS_DISABLE = new ErrorCode(1002003006, "名字为【{}】的用户已被禁用"); + ErrorCode USER_COUNT_MAX = new ErrorCode(1002003008, "创建用户失败,原因:超过租户最大租户配额({})!"); + + // ========== 部门模块 1002004000 ========== + ErrorCode DEPT_NAME_DUPLICATE = new ErrorCode(1002004000, "已经存在该名字的部门"); + ErrorCode DEPT_PARENT_NOT_EXITS = new ErrorCode(1002004001,"父级部门不存在"); + ErrorCode DEPT_NOT_FOUND = new ErrorCode(1002004002, "当前部门不存在"); + ErrorCode DEPT_EXITS_CHILDREN = new ErrorCode(1002004003, "存在子部门,无法删除"); + ErrorCode DEPT_PARENT_ERROR = new ErrorCode(1002004004, "不能设置自己为父部门"); + ErrorCode DEPT_EXISTS_USER = new ErrorCode(1002004005, "部门中存在员工,无法删除"); + ErrorCode DEPT_NOT_ENABLE = new ErrorCode(1002004006, "部门不处于开启状态,不允许选择"); + ErrorCode DEPT_PARENT_IS_CHILD = new ErrorCode(1002004007, "不能设置自己的子部门为父部门"); + + // ========== 岗位模块 1002005000 ========== + ErrorCode POST_NOT_FOUND = new ErrorCode(1002005000, "当前岗位不存在"); + ErrorCode POST_NOT_ENABLE = new ErrorCode(1002005001, "岗位({}) 不处于开启状态,不允许选择"); + ErrorCode POST_NAME_DUPLICATE = new ErrorCode(1002005002, "已经存在该名字的岗位"); + ErrorCode POST_CODE_DUPLICATE = new ErrorCode(1002005003, "已经存在该标识的岗位"); + + // ========== 字典类型 1002006000 ========== + ErrorCode DICT_TYPE_NOT_EXISTS = new ErrorCode(1002006001, "当前字典类型不存在"); + ErrorCode DICT_TYPE_NOT_ENABLE = new ErrorCode(1002006002, "字典类型不处于开启状态,不允许选择"); + ErrorCode DICT_TYPE_NAME_DUPLICATE = new ErrorCode(1002006003, "已经存在该名字的字典类型"); + ErrorCode DICT_TYPE_TYPE_DUPLICATE = new ErrorCode(1002006004, "已经存在该类型的字典类型"); + ErrorCode DICT_TYPE_HAS_CHILDREN = new ErrorCode(1002006005, "无法删除,该字典类型还有字典数据"); + + // ========== 字典数据 1002007000 ========== + ErrorCode DICT_DATA_NOT_EXISTS = new ErrorCode(1002007001, "当前字典数据不存在"); + ErrorCode DICT_DATA_NOT_ENABLE = new ErrorCode(1002007002, "字典数据({})不处于开启状态,不允许选择"); + ErrorCode DICT_DATA_VALUE_DUPLICATE = new ErrorCode(1002007003, "已经存在该值的字典数据"); + + // ========== 通知公告 1002008000 ========== + ErrorCode NOTICE_NOT_FOUND = new ErrorCode(1002008001, "当前通知公告不存在"); + + // ========== 短信渠道 1002011000 ========== + ErrorCode SMS_CHANNEL_NOT_EXISTS = new ErrorCode(1002011000, "短信渠道不存在"); + ErrorCode SMS_CHANNEL_DISABLE = new ErrorCode(1002011001, "短信渠道不处于开启状态,不允许选择"); + ErrorCode SMS_CHANNEL_HAS_CHILDREN = new ErrorCode(1002011002, "无法删除,该短信渠道还有短信模板"); + + // ========== 短信模板 1002012000 ========== + ErrorCode SMS_TEMPLATE_NOT_EXISTS = new ErrorCode(1002012000, "短信模板不存在"); + ErrorCode SMS_TEMPLATE_CODE_DUPLICATE = new ErrorCode(1002012001, "已经存在编码为【{}】的短信模板"); + + // ========== 短信发送 1002013000 ========== + ErrorCode SMS_SEND_MOBILE_NOT_EXISTS = new ErrorCode(1002013000, "手机号不存在"); + ErrorCode SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS = new ErrorCode(1002013001, "模板参数({})缺失"); + ErrorCode SMS_SEND_TEMPLATE_NOT_EXISTS = new ErrorCode(1002013002, "短信模板不存在"); + + // ========== 短信验证码 1002014000 ========== + ErrorCode SMS_CODE_NOT_FOUND = new ErrorCode(1002014000, "验证码不存在"); + ErrorCode SMS_CODE_EXPIRED = new ErrorCode(1002014001, "验证码已过期"); + ErrorCode SMS_CODE_USED = new ErrorCode(1002014002, "验证码已使用"); + ErrorCode SMS_CODE_NOT_CORRECT = new ErrorCode(1002014003, "验证码不正确"); + ErrorCode SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY = new ErrorCode(1002014004, "超过每日短信发送数量"); + ErrorCode SMS_CODE_SEND_TOO_FAST = new ErrorCode(1002014005, "短信发送过于频率"); + ErrorCode SMS_CODE_IS_EXISTS = new ErrorCode(1002014006, "手机号已被使用"); + ErrorCode SMS_CODE_IS_UNUSED = new ErrorCode(1002014007, "验证码未被使用"); + + // ========== 租户信息 1002015000 ========== + ErrorCode TENANT_NOT_EXISTS = new ErrorCode(1002015000, "租户不存在"); + ErrorCode TENANT_DISABLE = new ErrorCode(1002015001, "名字为【%s】的租户已被禁用"); + ErrorCode TENANT_EXPIRE = new ErrorCode(1002015002, "名字为【%s】的租户已过期"); + ErrorCode TENANT_CAN_NOT_UPDATE_SYSTEM = new ErrorCode(1002015003, "系统租户不能进行修改、删除等操作!"); + + // ========== 租户套餐 1002016000 ========== + ErrorCode TENANT_PACKAGE_NOT_EXISTS = new ErrorCode(1002016000, "租户套餐不存在"); + ErrorCode TENANT_PACKAGE_USED = new ErrorCode(1002016001, "租户正在使用该套餐,请给租户重新设置套餐后再尝试删除"); + ErrorCode TENANT_PACKAGE_DISABLE = new ErrorCode(1002016002, "名字为【{}】的租户套餐已被禁用"); + + // ========== 错误码模块 1002017000 ========== + ErrorCode ERROR_CODE_NOT_EXISTS = new ErrorCode(1002017000, "错误码不存在"); + ErrorCode ERROR_CODE_DUPLICATE = new ErrorCode(1002017001, "已经存在编码为【{}】的错误码"); + + // ========== 社交用户 1002018000 ========== + ErrorCode SOCIAL_USER_AUTH_FAILURE = new ErrorCode(1002018000, "社交授权失败,原因是:{}"); + ErrorCode SOCIAL_USER_UNBIND_NOT_SELF = new ErrorCode(1002018001, "社交解绑失败,非当前用户绑定"); + ErrorCode SOCIAL_USER_NOT_FOUND = new ErrorCode(1002018002, "社交授权失败,找不到对应的用户"); + + // ========== 系统敏感词 1002019000 ========= + ErrorCode SENSITIVE_WORD_NOT_EXISTS = new ErrorCode(1002019000, "系统敏感词在所有标签中都不存在"); + ErrorCode SENSITIVE_WORD_EXISTS = new ErrorCode(1002019001, "系统敏感词已在标签中存在"); + + // ========== OAuth2 客户端 1002020000 ========= + ErrorCode OAUTH2_CLIENT_NOT_EXISTS = new ErrorCode(1002020000, "OAuth2 客户端不存在"); + ErrorCode OAUTH2_CLIENT_EXISTS = new ErrorCode(1002020001, "OAuth2 客户端编号已存在"); + ErrorCode OAUTH2_CLIENT_DISABLE = new ErrorCode(1002020002, "OAuth2 客户端已禁用"); + ErrorCode OAUTH2_CLIENT_AUTHORIZED_GRANT_TYPE_NOT_EXISTS = new ErrorCode(1002020003, "不支持该授权类型"); + ErrorCode OAUTH2_CLIENT_SCOPE_OVER = new ErrorCode(1002020004, "授权范围过大"); + ErrorCode OAUTH2_CLIENT_REDIRECT_URI_NOT_MATCH = new ErrorCode(1002020005, "无效 redirect_uri: {}"); + ErrorCode OAUTH2_CLIENT_CLIENT_SECRET_ERROR = new ErrorCode(1002020006, "无效 client_secret: {}"); + + // ========== OAuth2 授权 1002021000 ========= + ErrorCode OAUTH2_GRANT_CLIENT_ID_MISMATCH = new ErrorCode(1002021000, "client_id 不匹配"); + ErrorCode OAUTH2_GRANT_REDIRECT_URI_MISMATCH = new ErrorCode(1002021001, "redirect_uri 不匹配"); + ErrorCode OAUTH2_GRANT_STATE_MISMATCH = new ErrorCode(1002021002, "state 不匹配"); + ErrorCode OAUTH2_GRANT_CODE_NOT_EXISTS = new ErrorCode(1002021003, "code 不存在"); + + // ========== OAuth2 授权 1002022000 ========= + ErrorCode OAUTH2_CODE_NOT_EXISTS = new ErrorCode(1002022000, "code 不存在"); + ErrorCode OAUTH2_CODE_EXPIRE = new ErrorCode(1002022000, "code 已过期"); + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/GenConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/GenConstants.java new file mode 100644 index 00000000..7d899d49 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/GenConstants.java @@ -0,0 +1,117 @@ +package com.ruoyi.common.constant; + +/** + * 代码生成通用常量 + * + * @author ruoyi + */ +public class GenConstants +{ + /** 单表(增删改查) */ + public static final String TPL_CRUD = "crud"; + + /** 树表(增删改查) */ + public static final String TPL_TREE = "tree"; + + /** 主子表(增删改查) */ + public static final String TPL_SUB = "sub"; + + /** 树编码字段 */ + public static final String TREE_CODE = "treeCode"; + + /** 树父编码字段 */ + public static final String TREE_PARENT_CODE = "treeParentCode"; + + /** 树名称字段 */ + public static final String TREE_NAME = "treeName"; + + /** 上级菜单ID字段 */ + public static final String PARENT_MENU_ID = "parentMenuId"; + + /** 上级菜单名称字段 */ + public static final String PARENT_MENU_NAME = "parentMenuName"; + + /** 数据库字符串类型 */ + public static final String[] COLUMNTYPE_STR = { "char", "varchar", "nvarchar", "varchar2" }; + + /** 数据库文本类型 */ + public static final String[] COLUMNTYPE_TEXT = { "tinytext", "text", "mediumtext", "longtext" }; + + /** 数据库时间类型 */ + public static final String[] COLUMNTYPE_TIME = { "datetime", "time", "date", "timestamp" }; + + /** 数据库数字类型 */ + public static final String[] COLUMNTYPE_NUMBER = { "tinyint", "smallint", "mediumint", "int", "number", "integer", + "bit", "bigint", "float", "double", "decimal" }; + + /** 页面不需要编辑字段 */ + public static final String[] COLUMNNAME_NOT_EDIT = { "id", "create_by", "create_time", "del_flag" }; + + /** 页面不需要显示的列表字段 */ + public static final String[] COLUMNNAME_NOT_LIST = { "id", "create_by", "create_time", "del_flag", "update_by", + "update_time" }; + + /** 页面不需要查询字段 */ + public static final String[] COLUMNNAME_NOT_QUERY = { "id", "create_by", "create_time", "del_flag", "update_by", + "update_time", "remark" }; + + /** Entity基类字段 */ + public static final String[] BASE_ENTITY = { "createBy", "createTime", "updateBy", "updateTime", "remark" }; + + /** Tree基类字段 */ + public static final String[] TREE_ENTITY = { "parentName", "parentId", "orderNum", "ancestors", "children" }; + + /** 文本框 */ + public static final String HTML_INPUT = "input"; + + /** 文本域 */ + public static final String HTML_TEXTAREA = "textarea"; + + /** 下拉框 */ + public static final String HTML_SELECT = "select"; + + /** 单选框 */ + public static final String HTML_RADIO = "radio"; + + /** 复选框 */ + public static final String HTML_CHECKBOX = "checkbox"; + + /** 日期控件 */ + public static final String HTML_DATETIME = "datetime"; + + /** 图片上传控件 */ + public static final String HTML_IMAGE_UPLOAD = "imageUpload"; + + /** 文件上传控件 */ + public static final String HTML_FILE_UPLOAD = "fileUpload"; + + /** 富文本控件 */ + public static final String HTML_EDITOR = "editor"; + + /** 字符串类型 */ + public static final String TYPE_STRING = "String"; + + /** 整型 */ + public static final String TYPE_INTEGER = "Integer"; + + /** 长整型 */ + public static final String TYPE_LONG = "Long"; + + /** 浮点型 */ + public static final String TYPE_DOUBLE = "Double"; + + /** 高精度计算类型 */ + public static final String TYPE_BIGDECIMAL = "BigDecimal"; + + /** 时间类型 */ + public static final String TYPE_DATE = "Date"; + + /** 模糊查询 */ + public static final String QUERY_LIKE = "LIKE"; + + /** 相等查询 */ + public static final String QUERY_EQ = "EQ"; + + /** 需要 */ + public static final String REQUIRE = "1"; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/HttpStatus.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/HttpStatus.java new file mode 100644 index 00000000..54a24394 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/HttpStatus.java @@ -0,0 +1,93 @@ +package com.ruoyi.common.constant; + +/** + * 返回状态码 + * + * @author ruoyi + */ +public class HttpStatus { + /** + * 操作成功 + */ + public static final int SUCCESS = 200; + + /** + * 对象创建成功 + */ + public static final int CREATED = 201; + + /** + * 请求已经被接受 + */ + public static final int ACCEPTED = 202; + + /** + * 操作已经执行成功,但是没有返回数据 + */ + public static final int NO_CONTENT = 204; + + /** + * 资源已被移除 + */ + public static final int MOVED_PERM = 301; + + /** + * 重定向 + */ + public static final int SEE_OTHER = 303; + + /** + * 资源没有被修改 + */ + public static final int NOT_MODIFIED = 304; + + /** + * 参数列表错误(缺少,格式不匹配) + */ + public static final int BAD_REQUEST = 400; + + /** + * 未授权 + */ + public static final int UNAUTHORIZED = 401; + + /** + * 访问受限,授权过期 + */ + public static final int FORBIDDEN = 403; + + /** + * 资源,服务未找到 + */ + public static final int NOT_FOUND = 404; + + /** + * 不允许的http方法 + */ + public static final int BAD_METHOD = 405; + + /** + * 资源冲突,或者资源被锁 + */ + public static final int CONFLICT = 409; + + /** + * 不支持的数据,媒体类型 + */ + public static final int UNSUPPORTED_TYPE = 415; + + /** + * 系统内部错误 + */ + public static final int ERROR = 500; + + /** + * 接口未实现 + */ + public static final int NOT_IMPLEMENTED = 501; + + /** + * 系统警告消息 + */ + public static final int WARN = 601; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java new file mode 100644 index 00000000..62ad8154 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java @@ -0,0 +1,50 @@ +package com.ruoyi.common.constant; + +/** + * 任务调度通用常量 + * + * @author ruoyi + */ +public class ScheduleConstants +{ + public static final String TASK_CLASS_NAME = "TASK_CLASS_NAME"; + + /** 执行目标key */ + public static final String TASK_PROPERTIES = "TASK_PROPERTIES"; + + /** 默认 */ + public static final String MISFIRE_DEFAULT = "0"; + + /** 立即触发执行 */ + public static final String MISFIRE_IGNORE_MISFIRES = "1"; + + /** 触发一次执行 */ + public static final String MISFIRE_FIRE_AND_PROCEED = "2"; + + /** 不触发立即执行 */ + public static final String MISFIRE_DO_NOTHING = "3"; + + public enum Status + { + /** + * 正常 + */ + NORMAL("0"), + /** + * 暂停 + */ + PAUSE("1"); + + private String value; + + private Status(String value) + { + this.value = value; + } + + public String getValue() + { + return value; + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/SqlConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/SqlConstants.java new file mode 100644 index 00000000..85dcdbfa --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/SqlConstants.java @@ -0,0 +1,29 @@ +package com.ruoyi.common.constant; + +/** + * 数据库常量信息 + * + * @author wangzongrun + */ +public class SqlConstants { + /** + * 数据库排序 升序 + */ + public static final String ASC = "ASC"; + + /** + * 数据库排序 降序 + */ + public static final String DESC = "DESC"; + + /** + * 数据库 开始时间字段名 + */ + public static final String FIELD_NAME_BEGIN_TIME = "beginTime"; + + /** + * 数据库 开始时间字段名 + */ + public static final String FIELD_NAME_END_TIME = "endTime"; + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/SysErrorCodeConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/SysErrorCodeConstants.java new file mode 100644 index 00000000..83da849c --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/SysErrorCodeConstants.java @@ -0,0 +1,146 @@ +package com.ruoyi.common.constant; + + +import com.ruoyi.common.exception.ErrorCode; + +/** + * System 错误码枚举类 + *

+ * system 系统,使用 1-002-000-000 段 + */ +public interface SysErrorCodeConstants { + + // ========== AUTH 模块 1002000000 ========== + ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1002000000, "登录失败,账号密码不正确"); + ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1002000001, "登录失败,账号被禁用"); + ErrorCode AUTH_LOGIN_CAPTCHA_NOT_FOUND = new ErrorCode(1002000003, "验证码不存在"); + ErrorCode AUTH_LOGIN_CAPTCHA_CODE_ERROR = new ErrorCode(1002000004, "验证码不正确"); + ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1002000005, "未绑定账号,需要进行绑定"); + ErrorCode AUTH_TOKEN_EXPIRED = new ErrorCode(1002000006, "Token 已经过期"); + ErrorCode AUTH_MOBILE_NOT_EXISTS = new ErrorCode(1002000007, "手机号不存在"); + + // ========== 菜单模块 1002001000 ========== + ErrorCode MENU_NAME_DUPLICATE = new ErrorCode(1002001000, "已经存在该名字的菜单"); + ErrorCode MENU_PARENT_NOT_EXISTS = new ErrorCode(1002001001, "父菜单不存在"); + ErrorCode MENU_PARENT_ERROR = new ErrorCode(1002001002, "不能设置自己为父菜单"); + ErrorCode MENU_NOT_EXISTS = new ErrorCode(1002001003, "菜单不存在"); + ErrorCode MENU_EXISTS_CHILDREN = new ErrorCode(1002001004, "存在子菜单,无法删除"); + ErrorCode MENU_PARENT_NOT_DIR_OR_MENU = new ErrorCode(1002001005, "父菜单的类型必须是目录或者菜单"); + + // ========== 角色模块 1002002000 ========== + ErrorCode ROLE_NOT_EXISTS = new ErrorCode(1002002000, "角色不存在"); + ErrorCode ROLE_NAME_DUPLICATE = new ErrorCode(1002002001, "已经存在名为【{}】的角色"); + ErrorCode ROLE_CODE_DUPLICATE = new ErrorCode(1002002002, "已经存在编码为【{}】的角色"); + ErrorCode ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE = new ErrorCode(1002002003, "不能操作类型为系统内置的角色"); + ErrorCode ROLE_IS_DISABLE = new ErrorCode(1002002004, "名字为【{}】的角色已被禁用"); + ErrorCode ROLE_ADMIN_CODE_ERROR = new ErrorCode(1002002005, "编码【{}】不能使用"); + + // ========== 用户模块 1002003000 ========== + ErrorCode USER_USERNAME_EXISTS = new ErrorCode(1002003000, "用户账号已经存在"); + ErrorCode USER_MOBILE_EXISTS = new ErrorCode(1002003001, "手机号已经存在"); + ErrorCode USER_EMAIL_EXISTS = new ErrorCode(1002003002, "邮箱已经存在"); + ErrorCode USER_NOT_EXISTS = new ErrorCode(1002003003, "用户不存在"); + ErrorCode USER_IMPORT_LIST_IS_EMPTY = new ErrorCode(1002003004, "导入用户数据不能为空!"); + ErrorCode USER_PASSWORD_FAILED = new ErrorCode(1002003005, "用户密码校验失败"); + ErrorCode USER_IS_DISABLE = new ErrorCode(1002003006, "名字为【{}】的用户已被禁用"); + ErrorCode USER_COUNT_MAX = new ErrorCode(1002003008, "创建用户失败,原因:超过租户最大租户配额({})!"); + + // ========== 部门模块 1002004000 ========== + ErrorCode DEPT_NAME_DUPLICATE = new ErrorCode(1002004000, "已经存在该名字的部门"); + ErrorCode DEPT_PARENT_NOT_EXITS = new ErrorCode(1002004001, "父级部门不存在"); + ErrorCode DEPT_NOT_FOUND = new ErrorCode(1002004002, "当前部门不存在"); + ErrorCode DEPT_EXITS_CHILDREN = new ErrorCode(1002004003, "存在子部门,无法删除"); + ErrorCode DEPT_PARENT_ERROR = new ErrorCode(1002004004, "不能设置自己为父部门"); + ErrorCode DEPT_EXISTS_USER = new ErrorCode(1002004005, "部门中存在员工,无法删除"); + ErrorCode DEPT_NOT_ENABLE = new ErrorCode(1002004006, "部门不处于开启状态,不允许选择"); + ErrorCode DEPT_PARENT_IS_CHILD = new ErrorCode(1002004007, "不能设置自己的子部门为父部门"); + + // ========== 岗位模块 1002005000 ========== + ErrorCode POST_NOT_FOUND = new ErrorCode(1002005000, "当前岗位不存在"); + ErrorCode POST_NOT_ENABLE = new ErrorCode(1002005001, "岗位({}) 不处于开启状态,不允许选择"); + ErrorCode POST_NAME_DUPLICATE = new ErrorCode(1002005002, "已经存在该名字的岗位"); + ErrorCode POST_CODE_DUPLICATE = new ErrorCode(1002005003, "已经存在该标识的岗位"); + + // ========== 字典类型 1002006000 ========== + ErrorCode DICT_TYPE_NOT_EXISTS = new ErrorCode(1002006001, "当前字典类型不存在"); + ErrorCode DICT_TYPE_NOT_ENABLE = new ErrorCode(1002006002, "字典类型不处于开启状态,不允许选择"); + ErrorCode DICT_TYPE_NAME_DUPLICATE = new ErrorCode(1002006003, "已经存在该名字的字典类型"); + ErrorCode DICT_TYPE_TYPE_DUPLICATE = new ErrorCode(1002006004, "已经存在该类型的字典类型"); + ErrorCode DICT_TYPE_HAS_CHILDREN = new ErrorCode(1002006005, "无法删除,该字典类型还有字典数据"); + + // ========== 字典数据 1002007000 ========== + ErrorCode DICT_DATA_NOT_EXISTS = new ErrorCode(1002007001, "当前字典数据不存在"); + ErrorCode DICT_DATA_NOT_ENABLE = new ErrorCode(1002007002, "字典数据({})不处于开启状态,不允许选择"); + ErrorCode DICT_DATA_VALUE_DUPLICATE = new ErrorCode(1002007003, "已经存在该值的字典数据"); + + // ========== 通知公告 1002008000 ========== + ErrorCode NOTICE_NOT_FOUND = new ErrorCode(1002008001, "当前通知公告不存在"); + + // ========== 短信渠道 1002011000 ========== + ErrorCode SMS_CHANNEL_NOT_EXISTS = new ErrorCode(1002011000, "短信渠道不存在"); + ErrorCode SMS_CHANNEL_DISABLE = new ErrorCode(1002011001, "短信渠道不处于开启状态,不允许选择"); + ErrorCode SMS_CHANNEL_HAS_CHILDREN = new ErrorCode(1002011002, "无法删除,该短信渠道还有短信模板"); + + // ========== 短信模板 1002012000 ========== + ErrorCode SMS_TEMPLATE_NOT_EXISTS = new ErrorCode(1002012000, "短信模板不存在"); + ErrorCode SMS_TEMPLATE_CODE_DUPLICATE = new ErrorCode(1002012001, "已经存在编码为【{}】的短信模板"); + + // ========== 短信发送 1002013000 ========== + ErrorCode SMS_SEND_MOBILE_NOT_EXISTS = new ErrorCode(1002013000, "手机号不存在"); + ErrorCode SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS = new ErrorCode(1002013001, "模板参数({})缺失"); + ErrorCode SMS_SEND_TEMPLATE_NOT_EXISTS = new ErrorCode(1002013002, "短信模板不存在"); + + // ========== 短信验证码 1002014000 ========== + ErrorCode SMS_CODE_NOT_FOUND = new ErrorCode(1002014000, "验证码不存在"); + ErrorCode SMS_CODE_EXPIRED = new ErrorCode(1002014001, "验证码已过期"); + ErrorCode SMS_CODE_USED = new ErrorCode(1002014002, "验证码已使用"); + ErrorCode SMS_CODE_NOT_CORRECT = new ErrorCode(1002014003, "验证码不正确"); + ErrorCode SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY = new ErrorCode(1002014004, "超过每日短信发送数量"); + ErrorCode SMS_CODE_SEND_TOO_FAST = new ErrorCode(1002014005, "短信发送过于频率"); + ErrorCode SMS_CODE_IS_EXISTS = new ErrorCode(1002014006, "手机号已被使用"); + ErrorCode SMS_CODE_IS_UNUSED = new ErrorCode(1002014007, "验证码未被使用"); + + // ========== 租户信息 1002015000 ========== + ErrorCode TENANT_NOT_EXISTS = new ErrorCode(1002015000, "租户不存在"); + ErrorCode TENANT_DISABLE = new ErrorCode(1002015001, "名字为【{}】的租户已被禁用"); + ErrorCode TENANT_EXPIRE = new ErrorCode(1002015002, "名字为【{}】的租户已过期"); + ErrorCode TENANT_CAN_NOT_UPDATE_SYSTEM = new ErrorCode(1002015003, "系统租户不能进行修改、删除等操作!"); + + // ========== 租户套餐 1002016000 ========== + ErrorCode TENANT_PACKAGE_NOT_EXISTS = new ErrorCode(1002016000, "租户套餐不存在"); + ErrorCode TENANT_PACKAGE_USED = new ErrorCode(1002016001, "租户正在使用该套餐,请给租户重新设置套餐后再尝试删除"); + ErrorCode TENANT_PACKAGE_DISABLE = new ErrorCode(1002016002, "名字为【{}】的租户套餐已被禁用"); + + // ========== 错误码模块 1002017000 ========== + ErrorCode ERROR_CODE_NOT_EXISTS = new ErrorCode(1002017000, "错误码不存在"); + ErrorCode ERROR_CODE_DUPLICATE = new ErrorCode(1002017001, "已经存在编码为【{}】的错误码"); + + // ========== 社交用户 1002018000 ========== + ErrorCode SOCIAL_USER_AUTH_FAILURE = new ErrorCode(1002018000, "社交授权失败,原因是:{}"); + ErrorCode SOCIAL_USER_UNBIND_NOT_SELF = new ErrorCode(1002018001, "社交解绑失败,非当前用户绑定"); + ErrorCode SOCIAL_USER_NOT_FOUND = new ErrorCode(1002018002, "社交授权失败,找不到对应的用户"); + + // ========== 系统敏感词 1002019000 ========= + ErrorCode SENSITIVE_WORD_NOT_EXISTS = new ErrorCode(1002019000, "系统敏感词在所有标签中都不存在"); + ErrorCode SENSITIVE_WORD_EXISTS = new ErrorCode(1002019001, "系统敏感词已在标签中存在"); + + // ========== OAuth2 客户端 1002020000 ========= + ErrorCode OAUTH2_CLIENT_NOT_EXISTS = new ErrorCode(1002020000, "OAuth2 客户端不存在"); + ErrorCode OAUTH2_CLIENT_EXISTS = new ErrorCode(1002020001, "OAuth2 客户端编号已存在"); + ErrorCode OAUTH2_CLIENT_DISABLE = new ErrorCode(1002020002, "OAuth2 客户端已禁用"); + ErrorCode OAUTH2_CLIENT_AUTHORIZED_GRANT_TYPE_NOT_EXISTS = new ErrorCode(1002020003, "不支持该授权类型"); + ErrorCode OAUTH2_CLIENT_SCOPE_OVER = new ErrorCode(1002020004, "授权范围过大"); + ErrorCode OAUTH2_CLIENT_REDIRECT_URI_NOT_MATCH = new ErrorCode(1002020005, "无效 redirect_uri: {}"); + ErrorCode OAUTH2_CLIENT_CLIENT_SECRET_ERROR = new ErrorCode(1002020006, "无效 client_secret: {}"); + + // ========== OAuth2 授权 1002021000 ========= + ErrorCode OAUTH2_GRANT_CLIENT_ID_MISMATCH = new ErrorCode(1002021000, "client_id 不匹配"); + ErrorCode OAUTH2_GRANT_REDIRECT_URI_MISMATCH = new ErrorCode(1002021001, "redirect_uri 不匹配"); + ErrorCode OAUTH2_GRANT_STATE_MISMATCH = new ErrorCode(1002021002, "state 不匹配"); + ErrorCode OAUTH2_GRANT_CODE_NOT_EXISTS = new ErrorCode(1002021003, "code 不存在"); + + // ========== OAuth2 授权 1002022000 ========= + ErrorCode OAUTH2_CODE_NOT_EXISTS = new ErrorCode(1002022000, "code 不存在"); + ErrorCode OAUTH2_CODE_EXPIRE = new ErrorCode(1002022000, "code 已过期"); + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/TreeConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/TreeConstants.java new file mode 100644 index 00000000..3752848b --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/TreeConstants.java @@ -0,0 +1,29 @@ +package com.ruoyi.common.constant; + +/** + * 树形通用常量信息 + * + * @author wangzongrun + */ +public class TreeConstants { + /** + * 无父级的情况下,祖籍的值 + */ + public static final String ANCESTORS_ROOT_VALUE = "0"; + + /** + * 无父级的情况下, + */ + public static final String PARENT_ROOT_VALUE = "0"; + + /** + * 无父级的情况下,层次默认值 + */ + public static final Integer LAYOUT_DEPTH_INIT_VALUE = 1; + + /** + * 层次码分隔符 + * .不能直接转义,添加转移字符 + */ + public static final String LAYOUT_CODE_SEPARATOR = "."; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/UserConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/UserConstants.java new file mode 100644 index 00000000..a936cd83 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/UserConstants.java @@ -0,0 +1,78 @@ +package com.ruoyi.common.constant; + +/** + * 用户常量信息 + * + * @author ruoyi + */ +public class UserConstants +{ + /** + * 平台内系统用户的唯一标志 + */ + public static final String SYS_USER = "SYS_USER"; + + /** 正常状态 */ + public static final String NORMAL = "0"; + + /** 异常状态 */ + public static final String EXCEPTION = "1"; + + /** 用户封禁状态 */ + public static final String USER_DISABLE = "1"; + + /** 角色封禁状态 */ + public static final String ROLE_DISABLE = "1"; + + /** 部门正常状态 */ + public static final String DEPT_NORMAL = "0"; + + /** 部门停用状态 */ + public static final String DEPT_DISABLE = "1"; + + /** 字典正常状态 */ + public static final String DICT_NORMAL = "0"; + + /** 是否为系统默认(是) */ + public static final String YES = "Y"; + + /** 是否菜单外链(是) */ + public static final String YES_FRAME = "0"; + + /** 是否菜单外链(否) */ + public static final String NO_FRAME = "1"; + + /** 菜单类型(目录) */ + public static final String TYPE_DIR = "M"; + + /** 菜单类型(菜单) */ + public static final String TYPE_MENU = "C"; + + /** 菜单类型(按钮) */ + public static final String TYPE_BUTTON = "F"; + + /** Layout组件标识 */ + public final static String LAYOUT = "Layout"; + + /** ParentView组件标识 */ + public final static String PARENT_VIEW = "ParentView"; + + /** InnerLink组件标识 */ + public final static String INNER_LINK = "InnerLink"; + + /** 校验返回结果码 */ + public final static String UNIQUE = "0"; + public final static String NOT_UNIQUE = "1"; + + /** + * 用户名长度限制 + */ + public static final int USERNAME_MIN_LENGTH = 2; + public static final int USERNAME_MAX_LENGTH = 20; + + /** + * 密码长度限制 + */ + public static final int PASSWORD_MIN_LENGTH = 5; + public static final int PASSWORD_MAX_LENGTH = 20; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/convert/DictConvert.java b/ruoyi-common/src/main/java/com/ruoyi/common/convert/DictConvert.java new file mode 100644 index 00000000..cf40ccdd --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/convert/DictConvert.java @@ -0,0 +1,71 @@ +package com.ruoyi.common.convert; + +import cn.hutool.core.convert.Convert; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.ruoyi.common.annotation.DictFormat; +import com.ruoyi.common.utils.DictUtils; +import lombok.extern.slf4j.Slf4j; + +/** + * Excel 数据字典转换器 + * + * @author 芋道源码 + */ +@Slf4j +public class DictConvert implements Converter { + + @Override + public Class supportJavaTypeKey() { + throw new UnsupportedOperationException("暂不支持,也不需要"); + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + throw new UnsupportedOperationException("暂不支持,也不需要"); + } + + @Override + public Object convertToJavaData(ReadCellData readCellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + // 使用字典解析 + String type = getType(contentProperty); + String label = readCellData.getStringValue(); + String value = DictUtils.getDictValue(type, label); + if (value == null) { + log.error("[convertToJavaData][type({}) 解析不掉 label({})]", type, label); + return null; + } + // 将 String 的 value 转换成对应的属性 + Class fieldClazz = contentProperty.getField().getType(); + return Convert.convert(fieldClazz, value); + } + + @Override + public WriteCellData convertToExcelData(Object object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + // 空时,返回空 + if (object == null) { + return new WriteCellData<>(""); + } + + // 使用字典格式化 + String type = getType(contentProperty); + String value = String.valueOf(object); + String label = DictUtils.getDictLabel(type, value); + if (label == null) { + log.error("[convertToExcelData][type({}) 转换不了 label({})]", type, value); + return new WriteCellData<>(""); + } + // 生成 Excel 小表格 + return new WriteCellData<>(label); + } + + private static String getType(ExcelContentProperty contentProperty) { + return contentProperty.getField().getAnnotation(DictFormat.class).value(); + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/convert/JsonConvert.java b/ruoyi-common/src/main/java/com/ruoyi/common/convert/JsonConvert.java new file mode 100644 index 00000000..5b512b3b --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/convert/JsonConvert.java @@ -0,0 +1,33 @@ +package com.ruoyi.common.convert; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.ruoyi.common.utils.JsonUtils; + +/** + * Excel Json 转换器 + * + * @author 芋道源码 + */ +public class JsonConvert implements Converter { + + @Override + public Class supportJavaTypeKey() { + throw new UnsupportedOperationException("暂不支持,也不需要"); + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + throw new UnsupportedOperationException("暂不支持,也不需要"); + } + + @Override + public WriteCellData convertToExcelData(Object value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + // 生成 Excel 小表格 + return new WriteCellData<>(JsonUtils.toJsonString(value)); + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java new file mode 100644 index 00000000..0645d64b --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java @@ -0,0 +1,236 @@ +package com.ruoyi.common.core.controller; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.metadata.OrderItem; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.constant.SqlConstants; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.page.PageDomain; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.core.page.TableSupport; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.PageUtils; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.sql.SqlUtil; +import org.apache.poi.ss.formula.functions.T; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; + +import java.beans.PropertyEditorSupport; +import java.util.Date; +import java.util.List; + +/** + * web层通用数据处理 + * + * @author ruoyi + */ +public class BaseController { + protected final Logger logger = LoggerFactory.getLogger(this.getClass()); + + /** + * 将前台传递过来的日期格式的字符串,自动转化为Date类型 + */ + @InitBinder + public void initBinder(WebDataBinder binder) { + // Date 类型转换 + binder.registerCustomEditor(Date.class, new PropertyEditorSupport() { + @Override + public void setAsText(String text) { + setValue(DateUtils.parseDate(text)); + } + }); + } + + /** + * 设置请求分页数据 + */ + protected void startPage() { + PageUtils.startPage(); + } + protected void startPage(String orderBy) { + PageUtils.startPage(orderBy); + } + /** + * 设置mybatis-plus请求分页数据 + */ + protected Page getPage(OrderItem... sorts) { + Page page = new Page<>(); + PageDomain pageDomain = TableSupport.buildPageRequest(); + Integer pageNum = pageDomain.getPageNum(); + Integer pageSize = pageDomain.getPageSize(); + String orderColumn = pageDomain.getOrderByColumn(); + String isAsc = pageDomain.getIsAsc(); + + if (StringUtils.isNotNull(pageNum) && StringUtils.isNotNull(pageSize)) { + page.setCurrent(pageNum); + page.setSize(pageSize); + page.setOptimizeCountSql(false); + page.setMaxLimit(500L); + } + //排序 + if (StringUtils.isNotBlank(orderColumn)) { + if (SqlConstants.ASC.equalsIgnoreCase(isAsc)) { + page.addOrder(OrderItem.asc(orderColumn)); + } else { + page.addOrder(OrderItem.desc(orderColumn)); + } + } + if (sorts.length >0) { + page.addOrder(sorts); + } + return page; + } + + /** + * mybatis-plus响应请求分页数据 + * + * @param page + */ + protected TableDataInfo getDataTable(IPage page) { + TableDataInfo rspData = new TableDataInfo(); + rspData.setCode(HttpStatus.SUCCESS); + rspData.setMsg("查询成功"); + rspData.setRows(page.getRecords()); + rspData.setTotal(page.getTotal()); + rspData.setPageNum(page.getCurrent()); + rspData.setPageSize(page.getSize()); + rspData.setTotalPageNum(page.getPages()); + return rspData; + } + + /** + * 设置请求排序数据 + */ + protected void startOrderBy() { + PageDomain pageDomain = TableSupport.buildPageRequest(); + if (StringUtils.isNotEmpty(pageDomain.getOrderBy())) { + String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy()); + PageHelper.orderBy(orderBy); + } + } + + /** + * 清理分页的线程变量 + */ + protected void clearPage() { + PageUtils.clearPage(); + } + + /** + * 响应请求分页数据 + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + protected TableDataInfo getDataTable(List list) { + TableDataInfo rspData = new TableDataInfo(); + rspData.setCode(HttpStatus.SUCCESS); + rspData.setMsg("查询成功"); + rspData.setRows(list); + rspData.setTotal(new PageInfo(list).getTotal()); + return rspData; + } + + /** + * 返回成功 + */ + public AjaxResult success() { + return AjaxResult.success(); + } + + /** + * 返回失败消息 + */ + public AjaxResult error() { + return AjaxResult.error(); + } + + /** + * 返回成功消息 + */ + public AjaxResult success(String message) { + return AjaxResult.success(message); + } + + /** + * 返回成功消息 + */ + public AjaxResult success(Object data) { + return AjaxResult.success(data); + } + + /** + * 返回失败消息 + */ + public AjaxResult error(String message) { + return AjaxResult.error(message); + } + + /** + * 返回警告消息 + */ + public AjaxResult warn(String message) { + return AjaxResult.warn(message); + } + + /** + * 响应返回结果 + * + * @param rows 影响行数 + * @return 操作结果 + */ + protected AjaxResult toAjax(int rows) { + return rows > 0 ? AjaxResult.success() : AjaxResult.error(); + } + + /** + * 响应返回结果 + * + * @param result 结果 + * @return 操作结果 + */ + protected AjaxResult toAjax(boolean result) { + return result ? success() : error(); + } + + /** + * 页面跳转 + */ + public String redirect(String url) { + return StringUtils.format("redirect:{}", url); + } + + /** + * 获取用户缓存信息 + */ + public LoginUser getLoginUser() { + return SecurityUtils.getLoginUser(); + } + + /** + * 获取登录用户id + */ + public Long getUserId() { + return getLoginUser().getUserId(); + } + + /** + * 获取登录部门id + */ + public Long getDeptId() { + return getLoginUser().getDeptId(); + } + + /** + * 获取登录用户名 + */ + public String getUsername() { + return getLoginUser().getUsername(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/AjaxResult.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/AjaxResult.java new file mode 100644 index 00000000..3dc5afc9 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/AjaxResult.java @@ -0,0 +1,179 @@ +package com.ruoyi.common.core.domain; + +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.utils.StringUtils; + +import java.util.HashMap; + +/** + * 操作消息提醒 + * + * @author ruoyi + */ +public class AjaxResult extends HashMap { + private static final long serialVersionUID = 1L; + + /** + * 状态码 + */ + public static final String CODE_TAG = "code"; + + /** + * 返回内容 + */ + public static final String MSG_TAG = "msg"; + + /** + * 数据对象 + */ + public static final String DATA_TAG = "data"; + + /** + * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。 + */ + public AjaxResult() { + } + + /** + * 初始化一个新创建的 AjaxResult 对象 + * + * @param code 状态码 + * @param msg 返回内容 + */ + public AjaxResult(int code, String msg) { + super.put(CODE_TAG, code); + super.put(MSG_TAG, msg); + } + + /** + * 初始化一个新创建的 AjaxResult 对象 + * + * @param code 状态码 + * @param msg 返回内容 + * @param data 数据对象 + */ + public AjaxResult(int code, String msg, Object data) { + super.put(CODE_TAG, code); + super.put(MSG_TAG, msg); + if (StringUtils.isNotNull(data)) { + super.put(DATA_TAG, data); + } + } + + /** + * 返回成功消息 + * + * @return 成功消息 + */ + public static AjaxResult success() { + return AjaxResult.success("操作成功"); + } + + /** + * 返回成功数据 + * + * @return 成功消息 + */ + public static AjaxResult success(Object data) { + return AjaxResult.success("操作成功", data); + } + + /** + * 返回成功消息 + * + * @param msg 返回内容 + * @return 成功消息 + */ + public static AjaxResult success(String msg) { + return AjaxResult.success(msg, null); + } + + /** + * 返回成功消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 成功消息 + */ + public static AjaxResult success(String msg, Object data) { + return new AjaxResult(HttpStatus.SUCCESS, msg, data); + } + + /** + * 返回警告消息 + * + * @param msg 返回内容 + * @return 警告消息 + */ + public static AjaxResult warn(String msg) { + return AjaxResult.warn(msg, null); + } + + /** + * 返回警告消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 警告消息 + */ + public static AjaxResult warn(String msg, Object data) { + return new AjaxResult(HttpStatus.WARN, msg, data); + } + + /** + * 返回错误消息 + * + * @return + */ + public static AjaxResult error() { + return AjaxResult.error("操作失败"); + } + + /** + * 返回错误消息 + * + * @param msg 返回内容 + * @return 警告消息 + */ + public static AjaxResult error(String msg) { + return AjaxResult.error(msg, null); + } + + /** + * 返回错误消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 警告消息 + */ + public static AjaxResult error(String msg, Object data) { + return new AjaxResult(HttpStatus.ERROR, msg, data); + } + + /** + * 返回错误消息 + * + * @param code 状态码 + * @param msg 返回内容 + * @return 警告消息 + */ + public static AjaxResult error(int code, String msg) { + return new AjaxResult(code, msg, null); + } + + /** + * 方便链式调用 + * + * @param key 键 + * @param value 值 + * @return 数据对象 + */ + @Override + public AjaxResult put(String key, Object value) { + super.put(key, value); + return this; + } + public boolean isSuccess(){ + return super.get("code") != null && "200".equals(super.get("code").toString()); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java new file mode 100644 index 00000000..9ae0db89 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java @@ -0,0 +1,124 @@ +package com.ruoyi.common.core.domain; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.fasterxml.jackson.annotation.JsonFormat; + +/** + * Entity基类 + * + * @author ruoyi + */ +public class BaseEntity implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 搜索值 */ + @TableField(exist = false) + private String searchValue; + + /** 创建者 */ + @TableField(fill = FieldFill.INSERT) + private String createBy; + + /** 创建时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @TableField(fill = FieldFill.INSERT) + private Date createTime; + + /** 更新者 */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + /** 更新时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @TableField(fill = FieldFill.INSERT_UPDATE) + private Date updateTime; + + /** 备注 */ + @TableField(exist = false) + private String remark; + + /** 请求参数 */ + @TableField(exist = false) + private Map params; + + public String getSearchValue() + { + return searchValue; + } + + public void setSearchValue(String searchValue) + { + this.searchValue = searchValue; + } + + public String getCreateBy() + { + return createBy; + } + + public void setCreateBy(String createBy) + { + this.createBy = createBy; + } + + public Date getCreateTime() + { + return createTime; + } + + public void setCreateTime(Date createTime) + { + this.createTime = createTime; + } + + public String getUpdateBy() + { + return updateBy; + } + + public void setUpdateBy(String updateBy) + { + this.updateBy = updateBy; + } + + public Date getUpdateTime() + { + return updateTime; + } + + public void setUpdateTime(Date updateTime) + { + this.updateTime = updateTime; + } + + public String getRemark() + { + return remark; + } + + public void setRemark(String remark) + { + this.remark = remark; + } + + public Map getParams() + { + if (params == null) + { + params = new HashMap<>(); + } + return params; + } + + public void setParams(Map params) + { + this.params = params; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/DictTreeEntity.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/DictTreeEntity.java new file mode 100644 index 00000000..16f21c82 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/DictTreeEntity.java @@ -0,0 +1,118 @@ +package com.ruoyi.common.core.domain; + +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.ruoyi.common.annotation.Excel; +import lombok.Data; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * Tree基类 + * + * @author ruoyi + */ +@Data +public class DictTreeEntity { + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @Excel(name = "主键") + @TableId(value = "ID", type = IdType.ASSIGN_ID) + private String id; + + /** + * 名称 + */ + @Excel(name = "名称") + private String label; + + /** + * 编码 + */ + @Excel(name = "编码") + private String code; + + /** + * 备注 + */ + @Excel(name = "备注") + private String remark; + + /** + * 父节点ID + */ + private String pid; + + /** + * 显示顺序 + */ + private Integer orderNum; + + /** + * 层次码 + * 说明: + * 顶级层次码为0 + * 非顶级使用4位数字显示,起始位1000 + */ + private String levelCode; + + /** + * 层次 + * 说明: + * 结合层次码,计算树形结构的的层次 + */ + private Integer levelDepth; + + /** + * 是否叶子节点 + *

+ * 说明: + * 新建节点,默认为末级节点,并更新父节点为非末级节点 + */ + private String isLeaf; + + /** + * 创建者 + */ + @TableField(fill = FieldFill.INSERT) + private String createBy; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** + * 更新者 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + /** + * 删除标志 + */ + @TableLogic + private String delFlag; + + /** + * 子节点 + */ + @TableField(exist = false) + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private List children = new ArrayList<>(); +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/R.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/R.java new file mode 100644 index 00000000..a1290d99 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/R.java @@ -0,0 +1,102 @@ +package com.ruoyi.common.core.domain; + +import com.ruoyi.common.constant.HttpStatus; + +import java.io.Serializable; + +/** + * 响应信息主体 + * + * @author ruoyi + */ +public class R implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 成功 + */ + public static final int SUCCESS = HttpStatus.SUCCESS; + + /** + * 失败 + */ + public static final int FAIL = HttpStatus.ERROR; + + private int code; + + private String msg; + + private T data; + + public static R ok() { + return restResult(null, SUCCESS, "操作成功"); + } + + public static R ok(T data) { + return restResult(data, SUCCESS, "操作成功"); + } + + public static R ok(T data, String msg) { + return restResult(data, SUCCESS, msg); + } + + public static R fail() { + return restResult(null, FAIL, "操作失败"); + } + + public static R fail(String msg) { + return restResult(null, FAIL, msg); + } + + public static R fail(T data) { + return restResult(data, FAIL, "操作失败"); + } + + public static R fail(T data, String msg) { + return restResult(data, FAIL, msg); + } + + public static R fail(int code, String msg) { + return restResult(null, code, msg); + } + + private static R restResult(T data, int code, String msg) { + R apiResult = new R<>(); + apiResult.setCode(code); + apiResult.setData(data); + apiResult.setMsg(msg); + return apiResult; + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } + + public static Boolean isError(R ret) { + return !isSuccess(ret); + } + + public static Boolean isSuccess(R ret) { + return R.SUCCESS == ret.getCode(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TenantBaseDO.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TenantBaseDO.java new file mode 100644 index 00000000..f5609b0a --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TenantBaseDO.java @@ -0,0 +1,20 @@ +package com.ruoyi.common.core.domain; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 拓展多租户的 BaseDO 基类 + * + * @author 芋道源码 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public abstract class TenantBaseDO extends BaseEntity { + + /** + * 多租户编号 + */ + private Long tenantId; + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeEntity.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeEntity.java new file mode 100644 index 00000000..a180a18c --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeEntity.java @@ -0,0 +1,79 @@ +package com.ruoyi.common.core.domain; + +import java.util.ArrayList; +import java.util.List; + +/** + * Tree基类 + * + * @author ruoyi + */ +public class TreeEntity extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 父菜单名称 */ + private String parentName; + + /** 父菜单ID */ + private Long parentId; + + /** 显示顺序 */ + private Integer orderNum; + + /** 祖级列表 */ + private String ancestors; + + /** 子部门 */ + private List children = new ArrayList<>(); + + public String getParentName() + { + return parentName; + } + + public void setParentName(String parentName) + { + this.parentName = parentName; + } + + public Long getParentId() + { + return parentId; + } + + public void setParentId(Long parentId) + { + this.parentId = parentId; + } + + public Integer getOrderNum() + { + return orderNum; + } + + public void setOrderNum(Integer orderNum) + { + this.orderNum = orderNum; + } + + public String getAncestors() + { + return ancestors; + } + + public void setAncestors(String ancestors) + { + this.ancestors = ancestors; + } + + public List getChildren() + { + return children; + } + + public void setChildren(List children) + { + this.children = children; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java new file mode 100644 index 00000000..bd835db9 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java @@ -0,0 +1,77 @@ +package com.ruoyi.common.core.domain; + +import java.io.Serializable; +import java.util.List; +import java.util.stream.Collectors; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.ruoyi.common.core.domain.entity.SysDept; +import com.ruoyi.common.core.domain.entity.SysMenu; + +/** + * Treeselect树结构实体类 + * + * @author ruoyi + */ +public class TreeSelect implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 节点ID */ + private Long id; + + /** 节点名称 */ + private String label; + + /** 子节点 */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private List children; + + public TreeSelect() + { + + } + + public TreeSelect(SysDept dept) + { + this.id = dept.getDeptId(); + this.label = dept.getDeptName(); + this.children = dept.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + public TreeSelect(SysMenu menu) + { + this.id = menu.getMenuId(); + this.label = menu.getMenuName(); + this.children = menu.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + public Long getId() + { + return id; + } + + public void setId(Long id) + { + this.id = id; + } + + public String getLabel() + { + return label; + } + + public void setLabel(String label) + { + this.label = label; + } + + public List getChildren() + { + return children; + } + + public void setChildren(List children) + { + this.children = children; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java new file mode 100644 index 00000000..16ffd7f8 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java @@ -0,0 +1,221 @@ +package com.ruoyi.common.core.domain.entity; + +import java.util.ArrayList; +import java.util.List; +import javax.validation.constraints.Email; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 部门表 sys_dept + * + * @author ruoyi + */ +public class SysDept extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 部门ID */ + @TableId(type = IdType.AUTO) + private Long deptId; + + /** 父部门ID */ + private Long parentId; + + /** 祖级列表 */ + private String ancestors; + + /** 部门名称 */ + private String deptName; + + /** 显示顺序 */ + private Integer orderNum; + + /** 负责人 */ + private String leader; + + /** 联系电话 */ + private String phone; + + /** 邮箱 */ + private String email; + + /** 部门状态:0正常,1停用 */ + private String status; + + /** 删除标志(0代表存在 2代表删除) */ + private String delFlag; + + /** 父部门名称 */ + @TableField(exist = false) + private String parentName; + /** + * 多租户编号 + */ + private Long tenantId; + /** 子部门 */ + @TableField(exist = false) + private List children = new ArrayList(); + + public Long getDeptId() + { + return deptId; + } + + public void setDeptId(Long deptId) + { + this.deptId = deptId; + } + + public Long getParentId() + { + return parentId; + } + + public void setParentId(Long parentId) + { + this.parentId = parentId; + } + + public String getAncestors() + { + return ancestors; + } + + public void setAncestors(String ancestors) + { + this.ancestors = ancestors; + } + + @NotBlank(message = "部门名称不能为空") + @Size(min = 0, max = 30, message = "部门名称长度不能超过30个字符") + public String getDeptName() + { + return deptName; + } + + public void setDeptName(String deptName) + { + this.deptName = deptName; + } + + @NotNull(message = "显示顺序不能为空") + public Integer getOrderNum() + { + return orderNum; + } + + public void setOrderNum(Integer orderNum) + { + this.orderNum = orderNum; + } + + public String getLeader() + { + return leader; + } + + public void setLeader(String leader) + { + this.leader = leader; + } + + @Size(min = 0, max = 11, message = "联系电话长度不能超过11个字符") + public String getPhone() + { + return phone; + } + + public void setPhone(String phone) + { + this.phone = phone; + } + + @Email(message = "邮箱格式不正确") + @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符") + public String getEmail() + { + return email; + } + + public void setEmail(String email) + { + this.email = email; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getDelFlag() + { + return delFlag; + } + + public void setDelFlag(String delFlag) + { + this.delFlag = delFlag; + } + + public String getParentName() + { + return parentName; + } + + public void setParentName(String parentName) + { + this.parentName = parentName; + } + + public List getChildren() + { + return children; + } + + public void setChildren(List children) + { + this.children = children; + } + + public Long getTenantId() { + return this.tenantId; + } + + public void setTenantId(Long tenantId) { + this.tenantId = tenantId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("deptId", getDeptId()) + .append("parentId", getParentId()) + .append("ancestors", getAncestors()) + .append("deptName", getDeptName()) + .append("orderNum", getOrderNum()) + .append("leader", getLeader()) + .append("phone", getPhone()) + .append("email", getEmail()) + .append("status", getStatus()) + .append("delFlag", getDelFlag()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java new file mode 100644 index 00000000..0d88bbcc --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java @@ -0,0 +1,180 @@ +package com.ruoyi.common.core.domain.entity; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 字典数据表 sys_dict_data + * + * @author ruoyi + */ +public class SysDictData extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 字典编码 */ + @Excel(name = "字典编码", cellType = ColumnType.NUMERIC) + @TableId(type = IdType.AUTO) + private Long dictCode; + + /** 字典排序 */ + @Excel(name = "字典排序", cellType = ColumnType.NUMERIC) + private Long dictSort; + + /** 字典标签 */ + @Excel(name = "字典标签") + private String dictLabel; + + /** 字典键值 */ + @Excel(name = "字典键值") + private String dictValue; + + /** 字典类型 */ + @Excel(name = "字典类型") + private String dictType; + + /** 样式属性(其他样式扩展) */ + private String cssClass; + + /** 表格字典样式 */ + private String listClass; + + /** 是否默认(Y是 N否) */ + @Excel(name = "是否默认", readConverterExp = "Y=是,N=否") + private String isDefault; + + /** 状态(0正常 1停用) */ + @Excel(name = "状态", readConverterExp = "0=正常,1=停用") + private String status; + + public Long getDictCode() + { + return dictCode; + } + + public void setDictCode(Long dictCode) + { + this.dictCode = dictCode; + } + + public Long getDictSort() + { + return dictSort; + } + + public void setDictSort(Long dictSort) + { + this.dictSort = dictSort; + } + + @NotBlank(message = "字典标签不能为空") + @Size(min = 0, max = 100, message = "字典标签长度不能超过100个字符") + public String getDictLabel() + { + return dictLabel; + } + + public void setDictLabel(String dictLabel) + { + this.dictLabel = dictLabel; + } + + @NotBlank(message = "字典键值不能为空") + @Size(min = 0, max = 100, message = "字典键值长度不能超过100个字符") + public String getDictValue() + { + return dictValue; + } + + public void setDictValue(String dictValue) + { + this.dictValue = dictValue; + } + + @NotBlank(message = "字典类型不能为空") + @Size(min = 0, max = 100, message = "字典类型长度不能超过100个字符") + public String getDictType() + { + return dictType; + } + + public void setDictType(String dictType) + { + this.dictType = dictType; + } + + @Size(min = 0, max = 100, message = "样式属性长度不能超过100个字符") + public String getCssClass() + { + return cssClass; + } + + public void setCssClass(String cssClass) + { + this.cssClass = cssClass; + } + + public String getListClass() + { + return listClass; + } + + public void setListClass(String listClass) + { + this.listClass = listClass; + } + + public boolean getDefault() + { + return UserConstants.YES.equals(this.isDefault); + } + + public String getIsDefault() + { + return isDefault; + } + + public void setIsDefault(String isDefault) + { + this.isDefault = isDefault; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("dictCode", getDictCode()) + .append("dictSort", getDictSort()) + .append("dictLabel", getDictLabel()) + .append("dictValue", getDictValue()) + .append("dictType", getDictType()) + .append("cssClass", getCssClass()) + .append("listClass", getListClass()) + .append("isDefault", getIsDefault()) + .append("status", getStatus()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java new file mode 100644 index 00000000..4c8b7424 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java @@ -0,0 +1,100 @@ +package com.ruoyi.common.core.domain.entity; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 字典类型表 sys_dict_type + * + * @author ruoyi + */ +public class SysDictType extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 字典主键 */ + @Excel(name = "字典主键", cellType = ColumnType.NUMERIC) + @TableId(type = IdType.AUTO) + private Long dictId; + + /** 字典名称 */ + @Excel(name = "字典名称") + private String dictName; + + /** 字典类型 */ + @Excel(name = "字典类型") + private String dictType; + + /** 状态(0正常 1停用) */ + @Excel(name = "状态", readConverterExp = "0=正常,1=停用") + private String status; + + public Long getDictId() + { + return dictId; + } + + public void setDictId(Long dictId) + { + this.dictId = dictId; + } + + @NotBlank(message = "字典名称不能为空") + @Size(min = 0, max = 100, message = "字典类型名称长度不能超过100个字符") + public String getDictName() + { + return dictName; + } + + public void setDictName(String dictName) + { + this.dictName = dictName; + } + + @NotBlank(message = "字典类型不能为空") + @Size(min = 0, max = 100, message = "字典类型类型长度不能超过100个字符") + @Pattern(regexp = "^[a-z][a-z0-9_]*$", message = "字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)") + public String getDictType() + { + return dictType; + } + + public void setDictType(String dictType) + { + this.dictType = dictType; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("dictId", getDictId()) + .append("dictName", getDictName()) + .append("dictType", getDictType()) + .append("status", getStatus()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java new file mode 100644 index 00000000..44eae08f --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java @@ -0,0 +1,252 @@ +package com.ruoyi.common.core.domain.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.ruoyi.common.core.domain.BaseEntity; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.util.ArrayList; +import java.util.List; + +/** + * 菜单权限表 sys_menu + * + * @author ruoyi + */ +public class SysMenu extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * 菜单ID + */ + @TableId(type = IdType.AUTO) + private Long menuId; + + /** + * 菜单名称 + */ + private String menuName; + + /** + * 父菜单ID + */ + private Long parentId; + + /** + * 显示顺序 + */ + private Integer orderNum; + + /** + * 路由地址 + */ + private String path; + + /** + * 组件路径 + */ + private String component; + + /** + * 路由参数 + */ + private String query; + + /** + * 是否为外链(0是 1否) + */ + private String isFrame; + + /** + * 是否缓存(0缓存 1不缓存) + */ + private String isCache; + + /** + * 类型(M目录 C菜单 F按钮) + */ + private String menuType; + + /** + * 显示状态(0显示 1隐藏) + */ + private String visible; + + /** + * 菜单状态(0正常 1停用) + */ + private String status; + + /** + * 权限字符串 + */ + private String perms; + + /** + * 菜单图标 + */ + private String icon; + + /** + * 子菜单 + */ + @TableField(exist = false) + private List children = new ArrayList(); + + public Long getMenuId() { + return menuId; + } + + public void setMenuId(Long menuId) { + this.menuId = menuId; + } + + @NotBlank(message = "菜单名称不能为空") + @Size(min = 0, max = 50, message = "菜单名称长度不能超过50个字符") + public String getMenuName() { + return menuName; + } + + public void setMenuName(String menuName) { + this.menuName = menuName; + } + + + public Long getParentId() { + return parentId; + } + + public void setParentId(Long parentId) { + this.parentId = parentId; + } + + @NotNull(message = "显示顺序不能为空") + public Integer getOrderNum() { + return orderNum; + } + + public void setOrderNum(Integer orderNum) { + this.orderNum = orderNum; + } + + @Size(min = 0, max = 200, message = "路由地址不能超过200个字符") + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + @Size(min = 0, max = 200, message = "组件路径不能超过255个字符") + public String getComponent() { + return component; + } + + public void setComponent(String component) { + this.component = component; + } + + public String getQuery() { + return query; + } + + public void setQuery(String query) { + this.query = query; + } + + public String getIsFrame() { + return isFrame; + } + + public void setIsFrame(String isFrame) { + this.isFrame = isFrame; + } + + public String getIsCache() { + return isCache; + } + + public void setIsCache(String isCache) { + this.isCache = isCache; + } + + @NotBlank(message = "菜单类型不能为空") + public String getMenuType() { + return menuType; + } + + public void setMenuType(String menuType) { + this.menuType = menuType; + } + + public String getVisible() { + return visible; + } + + public void setVisible(String visible) { + this.visible = visible; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + @Size(min = 0, max = 100, message = "权限标识长度不能超过100个字符") + public String getPerms() { + return perms; + } + + public void setPerms(String perms) { + this.perms = perms; + } + + public String getIcon() { + return icon; + } + + public void setIcon(String icon) { + this.icon = icon; + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } + + @Override + public String toString() { + return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) + .append("menuId", getMenuId()) + .append("menuName", getMenuName()) + .append("parentId", getParentId()) + .append("orderNum", getOrderNum()) + .append("path", getPath()) + .append("component", getComponent()) + .append("isFrame", getIsFrame()) + .append("IsCache", getIsCache()) + .append("menuType", getMenuType()) + .append("visible", getVisible()) + .append("status ", getStatus()) + .append("perms", getPerms()) + .append("icon", getIcon()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java new file mode 100644 index 00000000..bf07e8d1 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java @@ -0,0 +1,261 @@ +package com.ruoyi.common.core.domain.entity; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.core.domain.BaseEntity; + +import java.util.Set; + +/** + * 角色表 sys_role + * + * @author ruoyi + */ +public class SysRole extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 角色ID */ + @Excel(name = "角色序号", cellType = ColumnType.NUMERIC) + @TableId(type = IdType.AUTO) + private Long roleId; + + /** 角色名称 */ + @Excel(name = "角色名称") + private String roleName; + + /** 角色权限 */ + @Excel(name = "角色权限") + private String roleKey; + + /** 角色排序 */ + @Excel(name = "角色排序") + private String roleSort; + + /** 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限) */ + @Excel(name = "数据范围", readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限") + private String dataScope; + + /** 菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示) */ + private boolean menuCheckStrictly; + + /** 部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示 ) */ + private boolean deptCheckStrictly; + + /** 角色状态(0正常 1停用) */ + @Excel(name = "角色状态", readConverterExp = "0=正常,1=停用") + private String status; + + /** 删除标志(0代表存在 2代表删除) */ + private String delFlag; + + /** 用户是否存在此角色标识 默认不存在 */ + @TableField(exist = false) + private boolean flag = false; + + /** 菜单组 */ + @TableField(exist = false) + private Long[] menuIds; + + /** 部门组(数据权限) */ + @TableField(exist = false) + private Long[] deptIds; + + /** 角色菜单权限 */ + @TableField(exist = false) + private Set permissions; + /** + * 多租户编号 + */ + private Long tenantId; + public SysRole() + { + + } + + public SysRole(Long roleId) + { + this.roleId = roleId; + } + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + public boolean isAdmin() + { + return isAdmin(this.roleId); + } + + public static boolean isAdmin(Long roleId) + { + return roleId != null && 1L == roleId; + } + + @NotBlank(message = "角色名称不能为空") + @Size(min = 0, max = 30, message = "角色名称长度不能超过30个字符") + public String getRoleName() + { + return roleName; + } + + public void setRoleName(String roleName) + { + this.roleName = roleName; + } + + @NotBlank(message = "权限字符不能为空") + @Size(min = 0, max = 100, message = "权限字符长度不能超过100个字符") + public String getRoleKey() + { + return roleKey; + } + + public void setRoleKey(String roleKey) + { + this.roleKey = roleKey; + } + + @NotBlank(message = "显示顺序不能为空") + public String getRoleSort() + { + return roleSort; + } + + public void setRoleSort(String roleSort) + { + this.roleSort = roleSort; + } + + public String getDataScope() + { + return dataScope; + } + + public void setDataScope(String dataScope) + { + this.dataScope = dataScope; + } + + public boolean isMenuCheckStrictly() + { + return menuCheckStrictly; + } + + public void setMenuCheckStrictly(boolean menuCheckStrictly) + { + this.menuCheckStrictly = menuCheckStrictly; + } + + public boolean isDeptCheckStrictly() + { + return deptCheckStrictly; + } + + public void setDeptCheckStrictly(boolean deptCheckStrictly) + { + this.deptCheckStrictly = deptCheckStrictly; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getDelFlag() + { + return delFlag; + } + + public void setDelFlag(String delFlag) + { + this.delFlag = delFlag; + } + + public boolean isFlag() + { + return flag; + } + + public void setFlag(boolean flag) + { + this.flag = flag; + } + + public Long[] getMenuIds() + { + return menuIds; + } + + public void setMenuIds(Long[] menuIds) + { + this.menuIds = menuIds; + } + + public Long[] getDeptIds() + { + return deptIds; + } + + public void setDeptIds(Long[] deptIds) + { + this.deptIds = deptIds; + } + + public Set getPermissions() + { + return permissions; + } + + public void setPermissions(Set permissions) + { + this.permissions = permissions; + } + + public Long getTenantId() { + return this.tenantId; + } + + public void setTenantId(Long tenantId) { + this.tenantId = tenantId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("roleId", getRoleId()) + .append("roleName", getRoleName()) + .append("roleKey", getRoleKey()) + .append("roleSort", getRoleSort()) + .append("dataScope", getDataScope()) + .append("menuCheckStrictly", isMenuCheckStrictly()) + .append("deptCheckStrictly", isDeptCheckStrictly()) + .append("status", getStatus()) + .append("delFlag", getDelFlag()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java new file mode 100644 index 00000000..de21286e --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java @@ -0,0 +1,345 @@ +package com.ruoyi.common.core.domain.entity; + +import java.util.Date; +import java.util.List; +import javax.validation.constraints.*; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.annotation.Excel.Type; +import com.ruoyi.common.annotation.Excels; +import com.ruoyi.common.core.domain.BaseEntity; +import com.ruoyi.common.xss.Xss; + +/** + * 用户对象 sys_user + * + * @author ruoyi + */ +public class SysUser extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 用户ID */ + @Excel(name = "用户序号", cellType = ColumnType.NUMERIC, prompt = "用户编号") + @TableId(type = IdType.AUTO) + private Long userId; + + /** 部门ID */ + @Excel(name = "部门编号", type = Type.IMPORT) + private Long deptId; + + /** 用户账号 */ + @Excel(name = "登录名称") + private String userName; + + /** 用户昵称 */ + @Excel(name = "用户名称") + private String nickName; + + /** 用户邮箱 */ + @Excel(name = "用户邮箱") + private String email; + + /** 手机号码 */ + @Excel(name = "手机号码") + private String phonenumber; + + /** 用户性别 */ + @Excel(name = "用户性别", readConverterExp = "0=男,1=女,2=未知") + private String sex; + + /** 用户头像 */ + private String avatar; + + /** 密码 */ + private String password; + + /** 帐号状态(0正常 1停用) */ + @Excel(name = "帐号状态", readConverterExp = "0=正常,1=停用") + private String status; + + /** 删除标志(0代表存在 2代表删除) */ + private String delFlag; + + /** 最后登录IP */ + @Excel(name = "最后登录IP", type = Type.EXPORT) + private String loginIp; + + /** 最后登录时间 */ + @Excel(name = "最后登录时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Type.EXPORT) + private Date loginDate; + + /** 部门对象 */ + @Excels({ + @Excel(name = "部门名称", targetAttr = "deptName", type = Type.EXPORT), + @Excel(name = "部门负责人", targetAttr = "leader", type = Type.EXPORT) + }) + @TableField(exist = false) + private SysDept dept; + + /** 角色对象 */ + @TableField(exist = false) + private List roles; + + /** 角色组 */ + @TableField(exist = false) + private Long[] roleIds; + + /** 岗位组 */ + @TableField(exist = false) + private Long[] postIds; + + /** 角色ID */ + @TableField(exist = false) + private Long roleId; + /** + * 多租户编号 + */ + private Long tenantId; + public SysUser() + { + + } + + public SysUser(Long userId) + { + this.userId = userId; + } + + public Long getUserId() + { + return userId; + } + + public void setUserId(Long userId) + { + this.userId = userId; + } + + public boolean isAdmin() + { + return isAdmin(this.userId); + } + + public static boolean isAdmin(Long userId) + { + return userId != null && 1L == userId; + } + + public Long getDeptId() + { + return deptId; + } + + public void setDeptId(Long deptId) + { + this.deptId = deptId; + } + + @Xss(message = "用户昵称不能包含脚本字符") + @Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符") + public String getNickName() + { + return nickName; + } + + public void setNickName(String nickName) + { + this.nickName = nickName; + } + + @Xss(message = "用户账号不能包含脚本字符") + @NotBlank(message = "用户账号不能为空") + @Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符") + public String getUserName() + { + return userName; + } + + public void setUserName(String userName) + { + this.userName = userName; + } + + @Email(message = "邮箱格式不正确") + @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符") + public String getEmail() + { + return email; + } + + public void setEmail(String email) + { + this.email = email; + } + + @Size(min = 0, max = 11, message = "手机号码长度不能超过11个字符") + public String getPhonenumber() + { + return phonenumber; + } + + public void setPhonenumber(String phonenumber) + { + this.phonenumber = phonenumber; + } + + public String getSex() + { + return sex; + } + + public void setSex(String sex) + { + this.sex = sex; + } + + public String getAvatar() + { + return avatar; + } + + public void setAvatar(String avatar) + { + this.avatar = avatar; + } + + public String getPassword() + { + return password; + } + + public void setPassword(String password) + { + this.password = password; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getDelFlag() + { + return delFlag; + } + + public void setDelFlag(String delFlag) + { + this.delFlag = delFlag; + } + + public String getLoginIp() + { + return loginIp; + } + + public void setLoginIp(String loginIp) + { + this.loginIp = loginIp; + } + + public Date getLoginDate() + { + return loginDate; + } + + public void setLoginDate(Date loginDate) + { + this.loginDate = loginDate; + } + + public SysDept getDept() + { + return dept; + } + + public void setDept(SysDept dept) + { + this.dept = dept; + } + + public List getRoles() + { + return roles; + } + + public void setRoles(List roles) + { + this.roles = roles; + } + + public Long[] getRoleIds() + { + return roleIds; + } + + public void setRoleIds(Long[] roleIds) + { + this.roleIds = roleIds; + } + + public Long[] getPostIds() + { + return postIds; + } + + public void setPostIds(Long[] postIds) + { + this.postIds = postIds; + } + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + public Long getTenantId() { + return this.tenantId; + } + + public void setTenantId(Long tenantId) { + this.tenantId = tenantId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("userId", getUserId()) + .append("deptId", getDeptId()) + .append("userName", getUserName()) + .append("nickName", getNickName()) + .append("email", getEmail()) + .append("phonenumber", getPhonenumber()) + .append("sex", getSex()) + .append("avatar", getAvatar()) + .append("password", getPassword()) + .append("status", getStatus()) + .append("delFlag", getDelFlag()) + .append("loginIp", getLoginIp()) + .append("loginDate", getLoginDate()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .append("dept", getDept()) + .toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginBody.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginBody.java new file mode 100644 index 00000000..b5bc8c8e --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginBody.java @@ -0,0 +1,69 @@ +package com.ruoyi.common.core.domain.model; + +/** + * 用户登录对象 + * + * @author ruoyi + */ +public class LoginBody +{ + /** + * 用户名 + */ + private String username; + + /** + * 用户密码 + */ + private String password; + + /** + * 验证码 + */ + private String code; + + /** + * 唯一标识 + */ + private String uuid; + + public String getUsername() + { + return username; + } + + public void setUsername(String username) + { + this.username = username; + } + + public String getPassword() + { + return password; + } + + public void setPassword(String password) + { + this.password = password; + } + + public String getCode() + { + return code; + } + + public void setCode(String code) + { + this.code = code; + } + + public String getUuid() + { + return uuid; + } + + public void setUuid(String uuid) + { + this.uuid = uuid; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java new file mode 100644 index 00000000..79dba4e7 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java @@ -0,0 +1,295 @@ +package com.ruoyi.common.core.domain.model; + +import com.alibaba.fastjson2.annotation.JSONField; +import com.ruoyi.common.core.domain.entity.SysUser; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +/** + * 登录用户身份权限 + * + * @author ruoyi + */ +public class LoginUser implements UserDetails +{ + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + private Long userId; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 用户唯一标识 + */ + private String token; + + /** + * 登录时间 + */ + private Long loginTime; + + /** + * 过期时间 + */ + private Long expireTime; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地点 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 权限列表 + */ + private Set permissions; + + /** + * 数据范围信息 + */ + private List dataScopes; + + /** + * 用户信息 + */ + private SysUser user; + /** + * 租户编号 + */ + private Long tenantId; + + public Long getUserId() + { + return userId; + } + + public void setUserId(Long userId) + { + this.userId = userId; + } + + public Long getDeptId() + { + return deptId; + } + + public void setDeptId(Long deptId) + { + this.deptId = deptId; + } + + public String getToken() + { + return token; + } + + public void setToken(String token) + { + this.token = token; + } + + public LoginUser() + { + } + + public LoginUser(SysUser user, Set permissions) + { + this.user = user; + this.permissions = permissions; + } + + public LoginUser(Long userId, Long deptId, SysUser user, Set permissions, List dataScopes,Long tenantId) + { + this.userId = userId; + this.deptId = deptId; + this.user = user; + this.permissions = permissions; + this.dataScopes = dataScopes; + this.tenantId = tenantId; + } + + @JSONField(serialize = false) + @Override + public String getPassword() + { + return user.getPassword(); + } + + @Override + public String getUsername() + { + return user.getUserName(); + } + + /** + * 账户是否未过期,过期无法验证 + */ + @JSONField(serialize = false) + @Override + public boolean isAccountNonExpired() + { + return true; + } + + /** + * 指定用户是否解锁,锁定的用户无法进行身份验证 + * + * @return + */ + @JSONField(serialize = false) + @Override + public boolean isAccountNonLocked() + { + return true; + } + + /** + * 指示是否已过期的用户的凭据(密码),过期的凭据防止认证 + * + * @return + */ + @JSONField(serialize = false) + @Override + public boolean isCredentialsNonExpired() + { + return true; + } + + /** + * 是否可用 ,禁用的用户不能身份验证 + * + * @return + */ + @JSONField(serialize = false) + @Override + public boolean isEnabled() + { + return true; + } + + public Long getLoginTime() + { + return loginTime; + } + + public void setLoginTime(Long loginTime) + { + this.loginTime = loginTime; + } + + public String getIpaddr() + { + return ipaddr; + } + + public void setIpaddr(String ipaddr) + { + this.ipaddr = ipaddr; + } + + public String getLoginLocation() + { + return loginLocation; + } + + public void setLoginLocation(String loginLocation) + { + this.loginLocation = loginLocation; + } + + public String getBrowser() + { + return browser; + } + + public void setBrowser(String browser) + { + this.browser = browser; + } + + public String getOs() + { + return os; + } + + public void setOs(String os) + { + this.os = os; + } + + public Long getExpireTime() + { + return expireTime; + } + + public void setExpireTime(Long expireTime) + { + this.expireTime = expireTime; + } + + public Set getPermissions() + { + return permissions; + } + + public void setPermissions(Set permissions) + { + this.permissions = permissions; + } + + public List getDataScopes() { + return dataScopes; + } + + public void setDataScopes(List dataScopes) { + this.dataScopes = dataScopes; + } + + public SysUser getUser() + { + return user; + } + + public void setUser(SysUser user) + { + this.user = user; + } + + public Long getTenantId() { + return this.tenantId; + } + + public void setTenantId(Long tenantId) { + this.tenantId = tenantId; + } + + @Override + public Collection getAuthorities() + { + return null; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/RegisterBody.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/RegisterBody.java new file mode 100644 index 00000000..868a1fc5 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/RegisterBody.java @@ -0,0 +1,11 @@ +package com.ruoyi.common.core.domain.model; + +/** + * 用户注册对象 + * + * @author ruoyi + */ +public class RegisterBody extends LoginBody +{ + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/page/PageDomain.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/page/PageDomain.java new file mode 100644 index 00000000..8966cb4c --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/page/PageDomain.java @@ -0,0 +1,101 @@ +package com.ruoyi.common.core.page; + +import com.ruoyi.common.utils.StringUtils; + +/** + * 分页数据 + * + * @author ruoyi + */ +public class PageDomain +{ + /** 当前记录起始索引 */ + private Integer pageNum; + + /** 每页显示记录数 */ + private Integer pageSize; + + /** 排序列 */ + private String orderByColumn; + + /** 排序的方向desc或者asc */ + private String isAsc = "asc"; + + /** 分页参数合理化 */ + private Boolean reasonable = true; + + public String getOrderBy() + { + if (StringUtils.isEmpty(orderByColumn)) + { + return ""; + } + return StringUtils.toUnderScoreCase(orderByColumn) + " " + isAsc; + } + + public Integer getPageNum() + { + return pageNum; + } + + public void setPageNum(Integer pageNum) + { + this.pageNum = pageNum; + } + + public Integer getPageSize() + { + return pageSize; + } + + public void setPageSize(Integer pageSize) + { + this.pageSize = pageSize; + } + + public String getOrderByColumn() + { + return orderByColumn; + } + + public void setOrderByColumn(String orderByColumn) + { + this.orderByColumn = orderByColumn; + } + + public String getIsAsc() + { + return isAsc; + } + + public void setIsAsc(String isAsc) + { + if (StringUtils.isNotEmpty(isAsc)) + { + // 兼容前端排序类型 + if ("ascending".equals(isAsc)) + { + isAsc = "asc"; + } + else if ("descending".equals(isAsc)) + { + isAsc = "desc"; + } + this.isAsc = isAsc; + } + } + + public Boolean getReasonable() + { + if (StringUtils.isNull(reasonable)) + { + return Boolean.TRUE; + } + return reasonable; + } + + public void setReasonable(Boolean reasonable) + { + this.reasonable = reasonable; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableDataInfo.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableDataInfo.java new file mode 100644 index 00000000..96294659 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableDataInfo.java @@ -0,0 +1,116 @@ +package com.ruoyi.common.core.page; + +import java.io.Serializable; +import java.util.List; + +/** + * 表格分页数据对象 + * + * @author ruoyi + */ +public class TableDataInfo implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 总记录数 */ + private long total; + + /** 列表数据 */ + private List rows; + + /** 消息状态码 */ + private int code; + + /** 消息内容 */ + private String msg; + /** 当前记录起始索引 */ + private long pageNum; + + /** 每页显示记录数 */ + private long pageSize; + + /** 总页数 */ + private long totalPageNum; + /** + * 表格数据对象 + */ + public TableDataInfo() + { + } + + /** + * 分页 + * + * @param list 列表数据 + * @param total 总记录数 + */ + public TableDataInfo(List list, int total) + { + this.rows = list; + this.total = total; + } + + public long getTotal() + { + return total; + } + + public void setTotal(long total) + { + this.total = total; + } + + public List getRows() + { + return rows; + } + + public void setRows(List rows) + { + this.rows = rows; + } + + public int getCode() + { + return code; + } + + public void setCode(int code) + { + this.code = code; + } + + public String getMsg() + { + return msg; + } + + public void setMsg(String msg) + { + this.msg = msg; + } + + public long getPageNum() { + return pageNum; + } + + public void setPageNum(long pageNum) { + this.pageNum = pageNum; + } + + public long getPageSize() { + return pageSize; + } + + public void setPageSize(long pageSize) { + this.pageSize = pageSize; + } + + public long getTotalPageNum() { + return totalPageNum; + } + + public void setTotalPageNum(long totalPageNum) { + this.totalPageNum = totalPageNum; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableSupport.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableSupport.java new file mode 100644 index 00000000..a120c300 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableSupport.java @@ -0,0 +1,56 @@ +package com.ruoyi.common.core.page; + +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.utils.ServletUtils; + +/** + * 表格数据处理 + * + * @author ruoyi + */ +public class TableSupport +{ + /** + * 当前记录起始索引 + */ + public static final String PAGE_NUM = "pageNum"; + + /** + * 每页显示记录数 + */ + public static final String PAGE_SIZE = "pageSize"; + + /** + * 排序列 + */ + public static final String ORDER_BY_COLUMN = "orderByColumn"; + + /** + * 排序的方向 "desc" 或者 "asc". + */ + public static final String IS_ASC = "isAsc"; + + /** + * 分页参数合理化 + */ + public static final String REASONABLE = "reasonable"; + + /** + * 封装分页对象 + */ + public static PageDomain getPageDomain() + { + PageDomain pageDomain = new PageDomain(); + pageDomain.setPageNum(Convert.toInt(ServletUtils.getParameter(PAGE_NUM), 1)); + pageDomain.setPageSize(Convert.toInt(ServletUtils.getParameter(PAGE_SIZE), 10)); + pageDomain.setOrderByColumn(ServletUtils.getParameter(ORDER_BY_COLUMN)); + pageDomain.setIsAsc(ServletUtils.getParameter(IS_ASC)); + pageDomain.setReasonable(ServletUtils.getParameterToBool(REASONABLE)); + return pageDomain; + } + + public static PageDomain buildPageRequest() + { + return getPageDomain(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/service/BaseServiceImpl.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/service/BaseServiceImpl.java new file mode 100644 index 00000000..7d3c417d --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/service/BaseServiceImpl.java @@ -0,0 +1,12 @@ +//package com.ruoyi.common.core.service; +// +//import com.baomidou.mybatisplus.core.mapper.BaseMapper; +//import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +//import org.springframework.beans.factory.annotation.Autowired; +// +//public class BaseServiceImpl, T> extends ServiceImpl implements IBaseService { +// @Autowired +// private BaseMapper mapper; +// +// +//} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/service/IBaseService.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/service/IBaseService.java new file mode 100644 index 00000000..592bcfa6 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/service/IBaseService.java @@ -0,0 +1,6 @@ +//package com.ruoyi.common.core.service; +// +//import com.baomidou.mybatisplus.extension.service.IService; +// +//public interface IBaseService extends IService { +//} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/text/CharsetKit.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/CharsetKit.java new file mode 100644 index 00000000..84124aac --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/CharsetKit.java @@ -0,0 +1,86 @@ +package com.ruoyi.common.core.text; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import com.ruoyi.common.utils.StringUtils; + +/** + * 字符集工具类 + * + * @author ruoyi + */ +public class CharsetKit +{ + /** ISO-8859-1 */ + public static final String ISO_8859_1 = "ISO-8859-1"; + /** UTF-8 */ + public static final String UTF_8 = "UTF-8"; + /** GBK */ + public static final String GBK = "GBK"; + + /** ISO-8859-1 */ + public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1); + /** UTF-8 */ + public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8); + /** GBK */ + public static final Charset CHARSET_GBK = Charset.forName(GBK); + + /** + * 转换为Charset对象 + * + * @param charset 字符集,为空则返回默认字符集 + * @return Charset + */ + public static Charset charset(String charset) + { + return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset); + } + + /** + * 转换字符串的字符集编码 + * + * @param source 字符串 + * @param srcCharset 源字符集,默认ISO-8859-1 + * @param destCharset 目标字符集,默认UTF-8 + * @return 转换后的字符集 + */ + public static String convert(String source, String srcCharset, String destCharset) + { + return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset)); + } + + /** + * 转换字符串的字符集编码 + * + * @param source 字符串 + * @param srcCharset 源字符集,默认ISO-8859-1 + * @param destCharset 目标字符集,默认UTF-8 + * @return 转换后的字符集 + */ + public static String convert(String source, Charset srcCharset, Charset destCharset) + { + if (null == srcCharset) + { + srcCharset = StandardCharsets.ISO_8859_1; + } + + if (null == destCharset) + { + destCharset = StandardCharsets.UTF_8; + } + + if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset)) + { + return source; + } + return new String(source.getBytes(srcCharset), destCharset); + } + + /** + * @return 系统字符集编码 + */ + public static String systemCharset() + { + return Charset.defaultCharset().name(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/text/Convert.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/Convert.java new file mode 100644 index 00000000..b82321cd --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/Convert.java @@ -0,0 +1,1000 @@ +package com.ruoyi.common.core.text; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.text.NumberFormat; +import java.util.Set; +import com.ruoyi.common.utils.StringUtils; +import org.apache.commons.lang3.ArrayUtils; + +/** + * 类型转换器 + * + * @author ruoyi + */ +public class Convert +{ + /** + * 转换为字符串
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static String toStr(Object value, String defaultValue) + { + if (null == value) + { + return defaultValue; + } + if (value instanceof String) + { + return (String) value; + } + return value.toString(); + } + + /** + * 转换为字符串
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static String toStr(Object value) + { + return toStr(value, null); + } + + /** + * 转换为字符
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Character toChar(Object value, Character defaultValue) + { + if (null == value) + { + return defaultValue; + } + if (value instanceof Character) + { + return (Character) value; + } + + final String valueStr = toStr(value, null); + return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0); + } + + /** + * 转换为字符
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Character toChar(Object value) + { + return toChar(value, null); + } + + /** + * 转换为byte
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Byte toByte(Object value, Byte defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Byte) + { + return (Byte) value; + } + if (value instanceof Number) + { + return ((Number) value).byteValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Byte.parseByte(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为byte
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Byte toByte(Object value) + { + return toByte(value, null); + } + + /** + * 转换为Short
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Short toShort(Object value, Short defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Short) + { + return (Short) value; + } + if (value instanceof Number) + { + return ((Number) value).shortValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Short.parseShort(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Short
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Short toShort(Object value) + { + return toShort(value, null); + } + + /** + * 转换为Number
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Number toNumber(Object value, Number defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Number) + { + return (Number) value; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return NumberFormat.getInstance().parse(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Number
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Number toNumber(Object value) + { + return toNumber(value, null); + } + + /** + * 转换为int
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Integer toInt(Object value, Integer defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Integer) + { + return (Integer) value; + } + if (value instanceof Number) + { + return ((Number) value).intValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Integer.parseInt(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为int
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Integer toInt(Object value) + { + return toInt(value, null); + } + + /** + * 转换为Integer数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static Integer[] toIntArray(String str) + { + return toIntArray(",", str); + } + + /** + * 转换为Long数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static Long[] toLongArray(String str) + { + return toLongArray(",", str); + } + + /** + * 转换为Integer数组
+ * + * @param split 分隔符 + * @param split 被转换的值 + * @return 结果 + */ + public static Integer[] toIntArray(String split, String str) + { + if (StringUtils.isEmpty(str)) + { + return new Integer[] {}; + } + String[] arr = str.split(split); + final Integer[] ints = new Integer[arr.length]; + for (int i = 0; i < arr.length; i++) + { + final Integer v = toInt(arr[i], 0); + ints[i] = v; + } + return ints; + } + + /** + * 转换为Long数组
+ * + * @param split 分隔符 + * @param str 被转换的值 + * @return 结果 + */ + public static Long[] toLongArray(String split, String str) + { + if (StringUtils.isEmpty(str)) + { + return new Long[] {}; + } + String[] arr = str.split(split); + final Long[] longs = new Long[arr.length]; + for (int i = 0; i < arr.length; i++) + { + final Long v = toLong(arr[i], null); + longs[i] = v; + } + return longs; + } + + /** + * 转换为String数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static String[] toStrArray(String str) + { + return toStrArray(",", str); + } + + /** + * 转换为String数组
+ * + * @param split 分隔符 + * @param split 被转换的值 + * @return 结果 + */ + public static String[] toStrArray(String split, String str) + { + return str.split(split); + } + + /** + * 转换为long
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Long toLong(Object value, Long defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Long) + { + return (Long) value; + } + if (value instanceof Number) + { + return ((Number) value).longValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).longValue(); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为long
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Long toLong(Object value) + { + return toLong(value, null); + } + + /** + * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Double toDouble(Object value, Double defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Double) + { + return (Double) value; + } + if (value instanceof Number) + { + return ((Number) value).doubleValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).doubleValue(); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Double toDouble(Object value) + { + return toDouble(value, null); + } + + /** + * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Float toFloat(Object value, Float defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Float) + { + return (Float) value; + } + if (value instanceof Number) + { + return ((Number) value).floatValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Float.parseFloat(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Float toFloat(Object value) + { + return toFloat(value, null); + } + + /** + * 转换为boolean
+ * String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Boolean toBool(Object value, Boolean defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Boolean) + { + return (Boolean) value; + } + String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + valueStr = valueStr.trim().toLowerCase(); + switch (valueStr) + { + case "true": + case "yes": + case "ok": + case "1": + return true; + case "false": + case "no": + case "0": + return false; + default: + return defaultValue; + } + } + + /** + * 转换为boolean
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Boolean toBool(Object value) + { + return toBool(value, null); + } + + /** + * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * + * @param clazz Enum的Class + * @param value 值 + * @param defaultValue 默认值 + * @return Enum + */ + public static > E toEnum(Class clazz, Object value, E defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (clazz.isAssignableFrom(value.getClass())) + { + @SuppressWarnings("unchecked") + E myE = (E) value; + return myE; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Enum.valueOf(clazz, valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * + * @param clazz Enum的Class + * @param value 值 + * @return Enum + */ + public static > E toEnum(Class clazz, Object value) + { + return toEnum(clazz, value, null); + } + + /** + * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static BigInteger toBigInteger(Object value, BigInteger defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof BigInteger) + { + return (BigInteger) value; + } + if (value instanceof Long) + { + return BigInteger.valueOf((Long) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return new BigInteger(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static BigInteger toBigInteger(Object value) + { + return toBigInteger(value, null); + } + + /** + * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof BigDecimal) + { + return (BigDecimal) value; + } + if (value instanceof Long) + { + return new BigDecimal((Long) value); + } + if (value instanceof Double) + { + return new BigDecimal((Double) value); + } + if (value instanceof Integer) + { + return new BigDecimal((Integer) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return new BigDecimal(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static BigDecimal toBigDecimal(Object value) + { + return toBigDecimal(value, null); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @return 字符串 + */ + public static String utf8Str(Object obj) + { + return str(obj, CharsetKit.CHARSET_UTF_8); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @param charsetName 字符集 + * @return 字符串 + */ + public static String str(Object obj, String charsetName) + { + return str(obj, Charset.forName(charsetName)); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @param charset 字符集 + * @return 字符串 + */ + public static String str(Object obj, Charset charset) + { + if (null == obj) + { + return null; + } + + if (obj instanceof String) + { + return (String) obj; + } + else if (obj instanceof byte[]) + { + return str((byte[]) obj, charset); + } + else if (obj instanceof Byte[]) + { + byte[] bytes = ArrayUtils.toPrimitive((Byte[]) obj); + return str(bytes, charset); + } + else if (obj instanceof ByteBuffer) + { + return str((ByteBuffer) obj, charset); + } + return obj.toString(); + } + + /** + * 将byte数组转为字符串 + * + * @param bytes byte数组 + * @param charset 字符集 + * @return 字符串 + */ + public static String str(byte[] bytes, String charset) + { + return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset)); + } + + /** + * 解码字节码 + * + * @param data 字符串 + * @param charset 字符集,如果此字段为空,则解码的结果取决于平台 + * @return 解码后的字符串 + */ + public static String str(byte[] data, Charset charset) + { + if (data == null) + { + return null; + } + + if (null == charset) + { + return new String(data); + } + return new String(data, charset); + } + + /** + * 将编码的byteBuffer数据转换为字符串 + * + * @param data 数据 + * @param charset 字符集,如果为空使用当前系统字符集 + * @return 字符串 + */ + public static String str(ByteBuffer data, String charset) + { + if (data == null) + { + return null; + } + + return str(data, Charset.forName(charset)); + } + + /** + * 将编码的byteBuffer数据转换为字符串 + * + * @param data 数据 + * @param charset 字符集,如果为空使用当前系统字符集 + * @return 字符串 + */ + public static String str(ByteBuffer data, Charset charset) + { + if (null == charset) + { + charset = Charset.defaultCharset(); + } + return charset.decode(data).toString(); + } + + // ----------------------------------------------------------------------- 全角半角转换 + /** + * 半角转全角 + * + * @param input String. + * @return 全角字符串. + */ + public static String toSBC(String input) + { + return toSBC(input, null); + } + + /** + * 半角转全角 + * + * @param input String + * @param notConvertSet 不替换的字符集合 + * @return 全角字符串. + */ + public static String toSBC(String input, Set notConvertSet) + { + char[] c = input.toCharArray(); + for (int i = 0; i < c.length; i++) + { + if (null != notConvertSet && notConvertSet.contains(c[i])) + { + // 跳过不替换的字符 + continue; + } + + if (c[i] == ' ') + { + c[i] = '\u3000'; + } + else if (c[i] < '\177') + { + c[i] = (char) (c[i] + 65248); + + } + } + return new String(c); + } + + /** + * 全角转半角 + * + * @param input String. + * @return 半角字符串 + */ + public static String toDBC(String input) + { + return toDBC(input, null); + } + + /** + * 替换全角为半角 + * + * @param text 文本 + * @param notConvertSet 不替换的字符集合 + * @return 替换后的字符 + */ + public static String toDBC(String text, Set notConvertSet) + { + char[] c = text.toCharArray(); + for (int i = 0; i < c.length; i++) + { + if (null != notConvertSet && notConvertSet.contains(c[i])) + { + // 跳过不替换的字符 + continue; + } + + if (c[i] == '\u3000') + { + c[i] = ' '; + } + else if (c[i] > '\uFF00' && c[i] < '\uFF5F') + { + c[i] = (char) (c[i] - 65248); + } + } + String returnString = new String(c); + + return returnString; + } + + /** + * 数字金额大写转换 先写个完整的然后将如零拾替换成零 + * + * @param n 数字 + * @return 中文大写数字 + */ + public static String digitUppercase(double n) + { + String[] fraction = { "角", "分" }; + String[] digit = { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" }; + String[][] unit = { { "元", "万", "亿" }, { "", "拾", "佰", "仟" } }; + + String head = n < 0 ? "负" : ""; + n = Math.abs(n); + + String s = ""; + for (int i = 0; i < fraction.length; i++) + { + s += (digit[(int) (Math.floor(n * 10 * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", ""); + } + if (s.length() < 1) + { + s = "整"; + } + int integerPart = (int) Math.floor(n); + + for (int i = 0; i < unit[0].length && integerPart > 0; i++) + { + String p = ""; + for (int j = 0; j < unit[1].length && n > 0; j++) + { + p = digit[integerPart % 10] + unit[1][j] + p; + integerPart = integerPart / 10; + } + s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s; + } + return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整"); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/text/StrFormatter.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/StrFormatter.java new file mode 100644 index 00000000..c78ac776 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/StrFormatter.java @@ -0,0 +1,92 @@ +package com.ruoyi.common.core.text; + +import com.ruoyi.common.utils.StringUtils; + +/** + * 字符串格式化 + * + * @author ruoyi + */ +public class StrFormatter +{ + public static final String EMPTY_JSON = "{}"; + public static final char C_BACKSLASH = '\\'; + public static final char C_DELIM_START = '{'; + public static final char C_DELIM_END = '}'; + + /** + * 格式化字符串
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ * + * @param strPattern 字符串模板 + * @param argArray 参数列表 + * @return 结果 + */ + public static String format(final String strPattern, final Object... argArray) + { + if (StringUtils.isEmpty(strPattern) || StringUtils.isEmpty(argArray)) + { + return strPattern; + } + final int strPatternLength = strPattern.length(); + + // 初始化定义好的长度以获得更好的性能 + StringBuilder sbuf = new StringBuilder(strPatternLength + 50); + + int handledPosition = 0; + int delimIndex;// 占位符所在位置 + for (int argIndex = 0; argIndex < argArray.length; argIndex++) + { + delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition); + if (delimIndex == -1) + { + if (handledPosition == 0) + { + return strPattern; + } + else + { // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果 + sbuf.append(strPattern, handledPosition, strPatternLength); + return sbuf.toString(); + } + } + else + { + if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH) + { + if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH) + { + // 转义符之前还有一个转义符,占位符依旧有效 + sbuf.append(strPattern, handledPosition, delimIndex - 1); + sbuf.append(Convert.utf8Str(argArray[argIndex])); + handledPosition = delimIndex + 2; + } + else + { + // 占位符被转义 + argIndex--; + sbuf.append(strPattern, handledPosition, delimIndex - 1); + sbuf.append(C_DELIM_START); + handledPosition = delimIndex + 1; + } + } + else + { + // 正常占位符 + sbuf.append(strPattern, handledPosition, delimIndex); + sbuf.append(Convert.utf8Str(argArray[argIndex])); + handledPosition = delimIndex + 2; + } + } + } + // 加入最后一个占位符后所有的字符 + sbuf.append(strPattern, handledPosition, strPattern.length()); + + return sbuf.toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessStatus.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessStatus.java new file mode 100644 index 00000000..10b7306f --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessStatus.java @@ -0,0 +1,20 @@ +package com.ruoyi.common.enums; + +/** + * 操作状态 + * + * @author ruoyi + * + */ +public enum BusinessStatus +{ + /** + * 成功 + */ + SUCCESS, + + /** + * 失败 + */ + FAIL, +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessType.java new file mode 100644 index 00000000..2e17c4a5 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessType.java @@ -0,0 +1,59 @@ +package com.ruoyi.common.enums; + +/** + * 业务操作类型 + * + * @author ruoyi + */ +public enum BusinessType +{ + /** + * 其它 + */ + OTHER, + + /** + * 新增 + */ + INSERT, + + /** + * 修改 + */ + UPDATE, + + /** + * 删除 + */ + DELETE, + + /** + * 授权 + */ + GRANT, + + /** + * 导出 + */ + EXPORT, + + /** + * 导入 + */ + IMPORT, + + /** + * 强退 + */ + FORCE, + + /** + * 生成代码 + */ + GENCODE, + + /** + * 清空数据 + */ + CLEAN, +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/CommonStatusEnum.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/CommonStatusEnum.java new file mode 100644 index 00000000..707cf90e --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/CommonStatusEnum.java @@ -0,0 +1,38 @@ +package com.ruoyi.common.enums; + +import com.ruoyi.common.utils.collection.IntArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 通用状态枚举 + * + * hasPermi + */ +@Getter +@AllArgsConstructor +public enum CommonStatusEnum implements IntArrayValuable { + + ENABLE(0, "开启"), + DISABLE(1, "关闭"); + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CommonStatusEnum::getStatus).toArray(); + + + /** + * 状态值 + */ + private final Integer status; + /** + * 状态名 + */ + private final String name; + + @Override + public int[] array() { + return ARRAYS; + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java new file mode 100644 index 00000000..0d945be5 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java @@ -0,0 +1,19 @@ +package com.ruoyi.common.enums; + +/** + * 数据源 + * + * @author ruoyi + */ +public enum DataSourceType +{ + /** + * 主库 + */ + MASTER, + + /** + * 从库 + */ + SLAVE +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/HttpMethod.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/HttpMethod.java new file mode 100644 index 00000000..be6f7392 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/HttpMethod.java @@ -0,0 +1,36 @@ +package com.ruoyi.common.enums; + +import java.util.HashMap; +import java.util.Map; +import org.springframework.lang.Nullable; + +/** + * 请求方式 + * + * @author ruoyi + */ +public enum HttpMethod +{ + GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE; + + private static final Map mappings = new HashMap<>(16); + + static + { + for (HttpMethod httpMethod : values()) + { + mappings.put(httpMethod.name(), httpMethod); + } + } + + @Nullable + public static HttpMethod resolve(@Nullable String method) + { + return (method != null ? mappings.get(method) : null); + } + + public boolean matches(String method) + { + return (this == resolve(method)); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/LimitType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/LimitType.java new file mode 100644 index 00000000..c609fd8a --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/LimitType.java @@ -0,0 +1,20 @@ +package com.ruoyi.common.enums; + +/** + * 限流类型 + * + * @author ruoyi + */ + +public enum LimitType +{ + /** + * 默认策略全局限流 + */ + DEFAULT, + + /** + * 根据请求者IP进行限流 + */ + IP +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/OperatorType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/OperatorType.java new file mode 100644 index 00000000..bdd143c1 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/OperatorType.java @@ -0,0 +1,24 @@ +package com.ruoyi.common.enums; + +/** + * 操作人类别 + * + * @author ruoyi + */ +public enum OperatorType +{ + /** + * 其它 + */ + OTHER, + + /** + * 后台用户 + */ + MANAGE, + + /** + * 手机端用户 + */ + MOBILE +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/RoleCodeEnum.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/RoleCodeEnum.java new file mode 100644 index 00000000..09ab14a3 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/RoleCodeEnum.java @@ -0,0 +1,30 @@ +package com.ruoyi.common.enums; + +import com.ruoyi.common.utils.ObjectUtils; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 角色标识枚举 + */ +@Getter +@AllArgsConstructor +public enum RoleCodeEnum { + + SUPER_ADMIN("admin", "超级管理员"), TENANT_ADMIN("tenant_admin", "租户管理员"), + ; + + /** + * 角色编码 + */ + private final String code; + /** + * 名字 + */ + private final String name; + + public static boolean isSuperAdmin(String code) { + return ObjectUtils.equalsAny(code, SUPER_ADMIN.getCode()); + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/UserStatus.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/UserStatus.java new file mode 100644 index 00000000..d7ff44a9 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/UserStatus.java @@ -0,0 +1,30 @@ +package com.ruoyi.common.enums; + +/** + * 用户状态 + * + * @author ruoyi + */ +public enum UserStatus +{ + OK("0", "正常"), DISABLE("1", "停用"), DELETED("2", "删除"); + + private final String code; + private final String info; + + UserStatus(String code, String info) + { + this.code = code; + this.info = info; + } + + public String getCode() + { + return code; + } + + public String getInfo() + { + return info; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/bs/DeviceStatusEnum.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/bs/DeviceStatusEnum.java new file mode 100644 index 00000000..25f1c0b7 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/bs/DeviceStatusEnum.java @@ -0,0 +1,27 @@ +package com.ruoyi.common.enums.bs; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 角色标识枚举 + */ +@Getter +@AllArgsConstructor +public enum DeviceStatusEnum { + + EXCEPTION(0, "异常"), + NORMAL(1, "正常"), + INVALID(1, "失效"), + ; + + /** + * 编码 + */ + private final Integer code; + /** + * 名称 + */ + private final String name; + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/bs/TaskCodeEnum.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/bs/TaskCodeEnum.java new file mode 100644 index 00000000..46806184 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/bs/TaskCodeEnum.java @@ -0,0 +1,25 @@ +package com.ruoyi.common.enums.bs; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 任务编码 + */ +@Getter +@AllArgsConstructor +public enum TaskCodeEnum { + + POINT("point", "点位巡查"), + ; + + /** + * 编码 + */ + private final String code; + /** + * 名称 + */ + private final String name; + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/bs/TaskExceptionStatusEnum.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/bs/TaskExceptionStatusEnum.java new file mode 100644 index 00000000..1e55dea5 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/bs/TaskExceptionStatusEnum.java @@ -0,0 +1,27 @@ +package com.ruoyi.common.enums.bs; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 角色标识枚举 + */ +@Getter +@AllArgsConstructor +public enum TaskExceptionStatusEnum { + + PENDING("pending", "待检"), + NORMAL("normal", "正常"), + EXCEPTION("exception", "异常"), + ; + + /** + * 编码 + */ + private final String code; + /** + * 名称 + */ + private final String name; + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/bs/TaskSettingStatusEnum.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/bs/TaskSettingStatusEnum.java new file mode 100644 index 00000000..18341e22 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/bs/TaskSettingStatusEnum.java @@ -0,0 +1,26 @@ +package com.ruoyi.common.enums.bs; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 角色标识枚举 + */ +@Getter +@AllArgsConstructor +public enum TaskSettingStatusEnum { + + NOTSET("notset", "未设置"), + SETED("seted", "已设置"), + ; + + /** + * 编码 + */ + private final String code; + /** + * 名称 + */ + private final String name; + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/bs/TaskTypeEnum.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/bs/TaskTypeEnum.java new file mode 100644 index 00000000..75eff0b9 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/bs/TaskTypeEnum.java @@ -0,0 +1,27 @@ +package com.ruoyi.common.enums.bs; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 任务编码 + */ +@Getter +@AllArgsConstructor +public enum TaskTypeEnum { + + STANDARD("standard", "标准"), + CUSTOM("custom", "定制"), + SELF("self", "自制"), + ; + + /** + * 编码 + */ + private final String code; + /** + * 名称 + */ + private final String name; + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/DemoModeException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/DemoModeException.java new file mode 100644 index 00000000..f6ad2ab4 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/DemoModeException.java @@ -0,0 +1,15 @@ +package com.ruoyi.common.exception; + +/** + * 演示模式异常 + * + * @author ruoyi + */ +public class DemoModeException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + public DemoModeException() + { + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/ErrorCode.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/ErrorCode.java new file mode 100644 index 00000000..43920680 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/ErrorCode.java @@ -0,0 +1,26 @@ +package com.ruoyi.common.exception; + +import lombok.Data; + +/** + * 错误码对象 + * TODO 错误码设计成对象的原因,为未来的 i18 国际化做准备 + */ +@Data +public class ErrorCode { + + /** + * 错误码 + */ + private final Integer code; + /** + * 错误提示 + */ + private final String msg; + + public ErrorCode(Integer code, String message) { + this.code = code; + this.msg = message; + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/GlobalException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/GlobalException.java new file mode 100644 index 00000000..81a71b5d --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/GlobalException.java @@ -0,0 +1,58 @@ +package com.ruoyi.common.exception; + +/** + * 全局异常 + * + * @author ruoyi + */ +public class GlobalException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + * + * 和 {@link CommonResult#getDetailMessage()} 一致的设计 + */ + private String detailMessage; + + /** + * 空构造方法,避免反序列化问题 + */ + public GlobalException() + { + } + + public GlobalException(String message) + { + this.message = message; + } + + public String getDetailMessage() + { + return detailMessage; + } + + public GlobalException setDetailMessage(String detailMessage) + { + this.detailMessage = detailMessage; + return this; + } + + @Override + public String getMessage() + { + return message; + } + + public GlobalException setMessage(String message) + { + this.message = message; + return this; + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/ServiceException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/ServiceException.java new file mode 100644 index 00000000..fcc7ab6e --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/ServiceException.java @@ -0,0 +1,74 @@ +package com.ruoyi.common.exception; + +/** + * 业务异常 + * + * @author ruoyi + */ +public final class ServiceException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + /** + * 错误码 + */ + private Integer code; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + * + * 和 {@link CommonResult#getDetailMessage()} 一致的设计 + */ + private String detailMessage; + + /** + * 空构造方法,避免反序列化问题 + */ + public ServiceException() + { + } + + public ServiceException(String message) + { + this.message = message; + } + + public ServiceException(String message, Integer code) + { + this.message = message; + this.code = code; + } + + public String getDetailMessage() + { + return detailMessage; + } + + @Override + public String getMessage() + { + return message; + } + + public Integer getCode() + { + return code; + } + + public ServiceException setMessage(String message) + { + this.message = message; + return this; + } + + public ServiceException setDetailMessage(String detailMessage) + { + this.detailMessage = detailMessage; + return this; + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/UtilException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/UtilException.java new file mode 100644 index 00000000..980fa465 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/UtilException.java @@ -0,0 +1,26 @@ +package com.ruoyi.common.exception; + +/** + * 工具类异常 + * + * @author ruoyi + */ +public class UtilException extends RuntimeException +{ + private static final long serialVersionUID = 8247610319171014183L; + + public UtilException(Throwable e) + { + super(e.getMessage(), e); + } + + public UtilException(String message) + { + super(message); + } + + public UtilException(String message, Throwable throwable) + { + super(message, throwable); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/base/BaseException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/base/BaseException.java new file mode 100644 index 00000000..b55d72e1 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/base/BaseException.java @@ -0,0 +1,97 @@ +package com.ruoyi.common.exception.base; + +import com.ruoyi.common.utils.MessageUtils; +import com.ruoyi.common.utils.StringUtils; + +/** + * 基础异常 + * + * @author ruoyi + */ +public class BaseException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + /** + * 所属模块 + */ + private String module; + + /** + * 错误码 + */ + private String code; + + /** + * 错误码对应的参数 + */ + private Object[] args; + + /** + * 错误消息 + */ + private String defaultMessage; + + public BaseException(String module, String code, Object[] args, String defaultMessage) + { + this.module = module; + this.code = code; + this.args = args; + this.defaultMessage = defaultMessage; + } + + public BaseException(String module, String code, Object[] args) + { + this(module, code, args, null); + } + + public BaseException(String module, String defaultMessage) + { + this(module, null, null, defaultMessage); + } + + public BaseException(String code, Object[] args) + { + this(null, code, args, null); + } + + public BaseException(String defaultMessage) + { + this(null, null, null, defaultMessage); + } + + @Override + public String getMessage() + { + String message = null; + if (!StringUtils.isEmpty(code)) + { + message = MessageUtils.message(code, args); + } + if (message == null) + { + message = defaultMessage; + } + return message; + } + + public String getModule() + { + return module; + } + + public String getCode() + { + return code; + } + + public Object[] getArgs() + { + return args; + } + + public String getDefaultMessage() + { + return defaultMessage; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileException.java new file mode 100644 index 00000000..871f09b5 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileException.java @@ -0,0 +1,19 @@ +package com.ruoyi.common.exception.file; + +import com.ruoyi.common.exception.base.BaseException; + +/** + * 文件信息异常类 + * + * @author ruoyi + */ +public class FileException extends BaseException +{ + private static final long serialVersionUID = 1L; + + public FileException(String code, Object[] args) + { + super("file", code, args, null); + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java new file mode 100644 index 00000000..70e0ec9b --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.file; + +/** + * 文件名称超长限制异常类 + * + * @author ruoyi + */ +public class FileNameLengthLimitExceededException extends FileException +{ + private static final long serialVersionUID = 1L; + + public FileNameLengthLimitExceededException(int defaultFileNameLength) + { + super("upload.filename.exceed.length", new Object[] { defaultFileNameLength }); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileSizeLimitExceededException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileSizeLimitExceededException.java new file mode 100644 index 00000000..ec6ab054 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileSizeLimitExceededException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.file; + +/** + * 文件名大小限制异常类 + * + * @author ruoyi + */ +public class FileSizeLimitExceededException extends FileException +{ + private static final long serialVersionUID = 1L; + + public FileSizeLimitExceededException(long defaultMaxSize) + { + super("upload.exceed.maxSize", new Object[] { defaultMaxSize }); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java new file mode 100644 index 00000000..8b2af16c --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java @@ -0,0 +1,81 @@ +package com.ruoyi.common.exception.file; + +import java.util.Arrays; +import org.apache.commons.fileupload.FileUploadException; + +/** + * 文件上传 误异常类 + * + * @author ruoyi + */ +public class InvalidExtensionException extends FileUploadException +{ + private static final long serialVersionUID = 1L; + + private String[] allowedExtension; + private String extension; + private String filename; + + public InvalidExtensionException(String[] allowedExtension, String extension, String filename) + { + super("文件[" + filename + "]后缀[" + extension + "]不正确,请上传" + Arrays.toString(allowedExtension) + "格式"); + this.allowedExtension = allowedExtension; + this.extension = extension; + this.filename = filename; + } + + public String[] getAllowedExtension() + { + return allowedExtension; + } + + public String getExtension() + { + return extension; + } + + public String getFilename() + { + return filename; + } + + public static class InvalidImageExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidImageExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidFlashExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidFlashExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidMediaExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidMediaExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidVideoExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidVideoExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/job/TaskException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/job/TaskException.java new file mode 100644 index 00000000..a567b408 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/job/TaskException.java @@ -0,0 +1,34 @@ +package com.ruoyi.common.exception.job; + +/** + * 计划策略异常 + * + * @author ruoyi + */ +public class TaskException extends Exception +{ + private static final long serialVersionUID = 1L; + + private Code code; + + public TaskException(String msg, Code code) + { + this(msg, code, null); + } + + public TaskException(String msg, Code code, Exception nestedEx) + { + super(msg, nestedEx); + this.code = code; + } + + public Code getCode() + { + return code; + } + + public enum Code + { + TASK_EXISTS, NO_TASK_EXISTS, TASK_ALREADY_STARTED, UNKNOWN, CONFIG_ERROR, TASK_NODE_NOT_AVAILABLE + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaException.java new file mode 100644 index 00000000..389dbc75 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.user; + +/** + * 验证码错误异常类 + * + * @author ruoyi + */ +public class CaptchaException extends UserException +{ + private static final long serialVersionUID = 1L; + + public CaptchaException() + { + super("user.jcaptcha.error", null); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java new file mode 100644 index 00000000..85f94861 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.user; + +/** + * 验证码失效异常类 + * + * @author ruoyi + */ +public class CaptchaExpireException extends UserException +{ + private static final long serialVersionUID = 1L; + + public CaptchaExpireException() + { + super("user.jcaptcha.expire", null); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserException.java new file mode 100644 index 00000000..c292d70e --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserException.java @@ -0,0 +1,18 @@ +package com.ruoyi.common.exception.user; + +import com.ruoyi.common.exception.base.BaseException; + +/** + * 用户信息异常类 + * + * @author ruoyi + */ +public class UserException extends BaseException +{ + private static final long serialVersionUID = 1L; + + public UserException(String code, Object[] args) + { + super("user", code, args, null); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java new file mode 100644 index 00000000..a7f3e5ff --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.user; + +/** + * 用户密码不正确或不符合规范异常类 + * + * @author ruoyi + */ +public class UserPasswordNotMatchException extends UserException +{ + private static final long serialVersionUID = 1L; + + public UserPasswordNotMatchException() + { + super("user.password.not.match", null); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java new file mode 100644 index 00000000..3f95c0b1 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.user; + +/** + * 用户错误最大次数异常类 + * + * @author ruoyi + */ +public class UserPasswordRetryLimitExceedException extends UserException +{ + private static final long serialVersionUID = 1L; + + public UserPasswordRetryLimitExceedException(int retryLimitCount, int lockTime) + { + super("user.password.retry.limit.exceed", new Object[] { retryLimitCount, lockTime }); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/util/ServiceExceptionUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/util/ServiceExceptionUtil.java new file mode 100644 index 00000000..767a62c4 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/util/ServiceExceptionUtil.java @@ -0,0 +1,124 @@ +package com.ruoyi.common.exception.util; + +import com.google.common.annotations.VisibleForTesting; +import com.ruoyi.common.exception.ErrorCode; +import com.ruoyi.common.exception.ServiceException; +import lombok.extern.slf4j.Slf4j; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * {@link ServiceException} 工具类 + *

+ * 目的在于,格式化异常信息提示。 + * 考虑到 String.format 在参数不正确时会报错,因此使用 {} 作为占位符,并使用 {@link #doFormat(int, String, Object...)} 方法来格式化 + *

+ * 因为 {@link #MESSAGES} 里面默认是没有异常信息提示的模板的,所以需要使用方自己初始化进去。目前想到的有几种方式: + *

+ * 1. 异常提示信息,写在枚举类中,例如说,cn.iocoder.oceans.user.api.constants.ErrorCodeEnum 类 + ServiceExceptionConfiguration + * 2. 异常提示信息,写在 .properties 等等配置文件 + * 3. 异常提示信息,写在 Apollo 等等配置中心中,从而实现可动态刷新 + * 4. 异常提示信息,存储在 db 等等数据库中,从而实现可动态刷新 + * + * @author wangzongrun + */ +@Slf4j +public class ServiceExceptionUtil { + + /** + * 错误码提示模板 + */ + private static final ConcurrentMap MESSAGES = new ConcurrentHashMap<>(); + + public static void putAll(Map messages) { + ServiceExceptionUtil.MESSAGES.putAll(messages); + } + + public static void put(Integer code, String message) { + ServiceExceptionUtil.MESSAGES.put(code, message); + } + + public static void delete(Integer code, String message) { + ServiceExceptionUtil.MESSAGES.remove(code, message); + } + + // ========== 和 ServiceException 的集成 ========== + + public static ServiceException exception(ErrorCode errorCode) { + String messagePattern = MESSAGES.getOrDefault(errorCode.getCode(), errorCode.getMsg()); + return exception0(errorCode.getCode(), messagePattern); + } + + public static ServiceException exception(ErrorCode errorCode, Object... params) { + String messagePattern = MESSAGES.getOrDefault(errorCode.getCode(), errorCode.getMsg()); + return exception0(errorCode.getCode(), messagePattern, params); + } + + /** + * 创建指定编号的 ServiceException 的异常 + * + * @param code 编号 + * @return 异常 + */ + public static ServiceException exception(Integer code) { + return exception0(code, MESSAGES.get(code)); + } + + /** + * 创建指定编号的 ServiceException 的异常 + * + * @param code 编号 + * @param params 消息提示的占位符对应的参数 + * @return 异常 + */ + public static ServiceException exception(Integer code, Object... params) { + return exception0(code, MESSAGES.get(code), params); + } + + public static ServiceException exception0(Integer code, String messagePattern, Object... params) { + String message = doFormat(code, messagePattern, params); + return new ServiceException(message, code); + } + + // ========== 格式化方法 ========== + + /** + * 将错误编号对应的消息使用 params 进行格式化。 + * + * @param code 错误编号 + * @param messagePattern 消息模版 + * @param params 参数 + * @return 格式化后的提示 + */ + @VisibleForTesting + public static String doFormat(int code, String messagePattern, Object... params) { + StringBuilder sbuf = new StringBuilder(messagePattern.length() + 50); + int i = 0; + int j; + int l; + for (l = 0; l < params.length; l++) { + j = messagePattern.indexOf("{}", i); + if (j == -1) { + log.error("[doFormat][参数过多:错误码({})|错误内容({})|参数({})", code, messagePattern, params); + if (i == 0) { + return messagePattern; + } else { + sbuf.append(messagePattern.substring(i)); + return sbuf.toString(); + } + } else { + sbuf.append(messagePattern, i, j); + sbuf.append(params[l]); + i = j + 2; + } + } + if (messagePattern.indexOf("{}", i) != -1) { + log.error("[doFormat][参数过少:错误码({})|错误内容({})|参数({})", code, messagePattern, params); + } + sbuf.append(messagePattern.substring(i)); + return sbuf.toString(); + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/ApiRequestFilter.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/ApiRequestFilter.java new file mode 100644 index 00000000..ea1bb9e8 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/ApiRequestFilter.java @@ -0,0 +1,30 @@ +package com.ruoyi.common.filter; + +import cn.hutool.core.util.StrUtil; +import lombok.RequiredArgsConstructor; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.http.HttpServletRequest; + +/** + * 过滤 /admin-api、/app-api 等 API 请求的过滤器 + * + * @author 芋道源码 + */ +@RequiredArgsConstructor +public abstract class ApiRequestFilter extends OncePerRequestFilter { + + + @Override + protected boolean shouldNotFilter(HttpServletRequest request) { + if (StrUtil.startWith(request.getRequestURI(),"/profile/",false)) { + //静态资源,不需要走TenantSecurityWebFilter过滤器 + return true; + } + // 只过滤 API 请求的地址 +// return !StrUtil.startWithAny(request.getRequestURI(), webProperties.getAdminApi().getPrefix(), +// webProperties.getAppApi().getPrefix()); + return false; + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/PropertyPreExcludeFilter.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/PropertyPreExcludeFilter.java new file mode 100644 index 00000000..a145789d --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/PropertyPreExcludeFilter.java @@ -0,0 +1,24 @@ +package com.ruoyi.common.filter; + +import com.alibaba.fastjson2.filter.SimplePropertyPreFilter; + +/** + * 排除JSON敏感属性 + * + * @author ruoyi + */ +public class PropertyPreExcludeFilter extends SimplePropertyPreFilter +{ + public PropertyPreExcludeFilter() + { + } + + public PropertyPreExcludeFilter addExcludes(String... filters) + { + for (int i = 0; i < filters.length; i++) + { + this.getExcludes().add(filters[i]); + } + return this; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java new file mode 100644 index 00000000..a1bcfe2a --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java @@ -0,0 +1,52 @@ +package com.ruoyi.common.filter; + +import java.io.IOException; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import org.springframework.http.MediaType; +import com.ruoyi.common.utils.StringUtils; + +/** + * Repeatable 过滤器 + * + * @author ruoyi + */ +public class RepeatableFilter implements Filter +{ + @Override + public void init(FilterConfig filterConfig) throws ServletException + { + + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + ServletRequest requestWrapper = null; + if (request instanceof HttpServletRequest + && StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)) + { + requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response); + } + if (null == requestWrapper) + { + chain.doFilter(request, response); + } + else + { + chain.doFilter(requestWrapper, response); + } + } + + @Override + public void destroy() + { + + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java new file mode 100644 index 00000000..407d1ba4 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java @@ -0,0 +1,76 @@ +package com.ruoyi.common.filter; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import com.ruoyi.common.utils.http.HttpHelper; +import com.ruoyi.common.constant.Constants; + +/** + * 构建可重复读取inputStream的request + * + * @author ruoyi + */ +public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper +{ + private final byte[] body; + + public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException + { + super(request); + request.setCharacterEncoding(Constants.UTF8); + response.setCharacterEncoding(Constants.UTF8); + + body = HttpHelper.getBodyString(request).getBytes(Constants.UTF8); + } + + @Override + public BufferedReader getReader() throws IOException + { + return new BufferedReader(new InputStreamReader(getInputStream())); + } + + @Override + public ServletInputStream getInputStream() throws IOException + { + final ByteArrayInputStream bais = new ByteArrayInputStream(body); + return new ServletInputStream() + { + @Override + public int read() throws IOException + { + return bais.read(); + } + + @Override + public int available() throws IOException + { + return body.length; + } + + @Override + public boolean isFinished() + { + return false; + } + + @Override + public boolean isReady() + { + return false; + } + + @Override + public void setReadListener(ReadListener readListener) + { + + } + }; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java new file mode 100644 index 00000000..9052f0da --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java @@ -0,0 +1,75 @@ +package com.ruoyi.common.filter; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.enums.HttpMethod; + +/** + * 防止XSS攻击的过滤器 + * + * @author ruoyi + */ +public class XssFilter implements Filter +{ + /** + * 排除链接 + */ + public List excludes = new ArrayList<>(); + + @Override + public void init(FilterConfig filterConfig) throws ServletException + { + String tempExcludes = filterConfig.getInitParameter("excludes"); + if (StringUtils.isNotEmpty(tempExcludes)) + { + String[] url = tempExcludes.split(","); + for (int i = 0; url != null && i < url.length; i++) + { + excludes.add(url[i]); + } + } + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + HttpServletRequest req = (HttpServletRequest) request; + HttpServletResponse resp = (HttpServletResponse) response; + if (handleExcludeURL(req, resp)) + { + chain.doFilter(request, response); + return; + } + XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request); + chain.doFilter(xssRequest, response); + } + + private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response) + { + String url = request.getServletPath(); + String method = request.getMethod(); + // GET DELETE 不过滤 + if (method == null || HttpMethod.GET.matches(method) || HttpMethod.DELETE.matches(method)) + { + return true; + } + return StringUtils.matches(url, excludes); + } + + @Override + public void destroy() + { + + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java new file mode 100644 index 00000000..b1eeb65c --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java @@ -0,0 +1,111 @@ +package com.ruoyi.common.filter; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import org.apache.commons.io.IOUtils; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.html.EscapeUtil; + +/** + * XSS过滤处理 + * + * @author ruoyi + */ +public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper +{ + /** + * @param request + */ + public XssHttpServletRequestWrapper(HttpServletRequest request) + { + super(request); + } + + @Override + public String[] getParameterValues(String name) + { + String[] values = super.getParameterValues(name); + if (values != null) + { + int length = values.length; + String[] escapseValues = new String[length]; + for (int i = 0; i < length; i++) + { + // 防xss攻击和过滤前后空格 + escapseValues[i] = EscapeUtil.clean(values[i]).trim(); + } + return escapseValues; + } + return super.getParameterValues(name); + } + + @Override + public ServletInputStream getInputStream() throws IOException + { + // 非json类型,直接返回 + if (!isJsonRequest()) + { + return super.getInputStream(); + } + + // 为空,直接返回 + String json = IOUtils.toString(super.getInputStream(), "utf-8"); + if (StringUtils.isEmpty(json)) + { + return super.getInputStream(); + } + + // xss过滤 + json = EscapeUtil.clean(json).trim(); + byte[] jsonBytes = json.getBytes("utf-8"); + final ByteArrayInputStream bis = new ByteArrayInputStream(jsonBytes); + return new ServletInputStream() + { + @Override + public boolean isFinished() + { + return true; + } + + @Override + public boolean isReady() + { + return true; + } + + @Override + public int available() throws IOException + { + return jsonBytes.length; + } + + @Override + public void setReadListener(ReadListener readListener) + { + } + + @Override + public int read() throws IOException + { + return bis.read(); + } + }; + } + + /** + * 是否是Json请求 + * + * @param request + */ + public boolean isJsonRequest() + { + String header = super.getHeader(HttpHeaders.CONTENT_TYPE); + return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE); + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/handler/JsonLongSetTypeHandler.java b/ruoyi-common/src/main/java/com/ruoyi/common/handler/JsonLongSetTypeHandler.java new file mode 100644 index 00000000..80d546bf --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/handler/JsonLongSetTypeHandler.java @@ -0,0 +1,31 @@ +package com.ruoyi.common.handler; + +import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler; +import com.fasterxml.jackson.core.type.TypeReference; +import com.ruoyi.common.utils.JsonUtils; + +import java.util.Set; + +/** + * 参考 {@link com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler} 实现 + * 在我们将字符串反序列化为 Set 并且泛型为 Long 时,如果每个元素的数值太小,会被处理成 Integer 类型,导致可能存在隐性的 BUG。 + * + * 例如说哦,SysUserDO 的 postIds 属性 + * + * hasPermi + */ +public class JsonLongSetTypeHandler extends AbstractJsonTypeHandler { + + private static final TypeReference> typeReference = new TypeReference>(){}; + + @Override + protected Object parse(String json) { + return JsonUtils.parseObject(json, typeReference); + } + + @Override + protected String toJson(Object obj) { + return JsonUtils.toJsonString(obj); + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/mybatis/dataobject/BaseDO.java b/ruoyi-common/src/main/java/com/ruoyi/common/mybatis/dataobject/BaseDO.java new file mode 100644 index 00000000..43bbb1d1 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/mybatis/dataobject/BaseDO.java @@ -0,0 +1,68 @@ +package com.ruoyi.common.mybatis.dataobject; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableLogic; +import lombok.Data; +import org.apache.ibatis.type.JdbcType; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * 基础实体对象 + * + * hasPermi + */ +@Data +public abstract class BaseDO implements Serializable { + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private Date createTime; + /** + * 最后更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Date updateTime; + /** + * 创建者,目前使用 SysUser 的 id 编号 + * + * 使用 String 类型的原因是,未来可能会存在非数值的情况,留好拓展性。 + */ + @TableField(fill = FieldFill.INSERT, jdbcType = JdbcType.VARCHAR) + private String creator; + /** + * 更新者,目前使用 SysUser 的 id 编号 + * + * 使用 String 类型的原因是,未来可能会存在非数值的情况,留好拓展性。 + */ + @TableField(fill = FieldFill.INSERT_UPDATE, jdbcType = JdbcType.VARCHAR) + private String updater; + /** + * 是否删除 + */ + @TableLogic + private Boolean deleted; + /** 请求参数 */ + @TableField(exist = false) + private Map params ; + + public Map getParams() + { + if (params == null) + { + params = new HashMap<>(); + } + return params; + } + + public void setParams(Map params) + { + this.params = params; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/mybatis/handler/DefaultDBFieldHandler.java b/ruoyi-common/src/main/java/com/ruoyi/common/mybatis/handler/DefaultDBFieldHandler.java new file mode 100644 index 00000000..b4797ca3 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/mybatis/handler/DefaultDBFieldHandler.java @@ -0,0 +1,87 @@ +package com.ruoyi.common.mybatis.handler; + +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import com.ruoyi.common.mybatis.dataobject.BaseDO; +import com.ruoyi.common.utils.SecurityUtils; +import org.apache.ibatis.reflection.MetaObject; + +import java.util.Date; +import java.util.Objects; + +/** + * 通用参数填充实现类 + *

+ * 如果没有显式的对通用参数进行赋值,这里会对通用参数进行填充、赋值 + * + * @author hexiaowu + */ +public class DefaultDBFieldHandler implements MetaObjectHandler { + + @Override + public void insertFill(MetaObject metaObject) { + if (Objects.nonNull(metaObject)) { + if (metaObject.getOriginalObject() instanceof BaseDO) { + BaseDO baseDO = (BaseDO) metaObject.getOriginalObject(); + + Date current = new Date(); + // 创建时间为空,则以当前时间为插入时间 + if (Objects.isNull(baseDO.getCreateTime())) { + baseDO.setCreateTime(current); + } + // 更新时间为空,则以当前时间为更新时间 + if (Objects.isNull(baseDO.getUpdateTime())) { + baseDO.setUpdateTime(current); + } + + Long userId = SecurityUtils.getLoginUserId(); + // 当前登录用户不为空,创建人为空,则当前登录用户为创建人 + if (Objects.nonNull(userId) && Objects.isNull(baseDO.getCreator())) { + baseDO.setCreator(userId.toString()); + } + // 当前登录用户不为空,更新人为空,则当前登录用户为更新人 + if (Objects.nonNull(userId) && Objects.isNull(baseDO.getUpdater())) { + baseDO.setUpdater(userId.toString()); + } + } + //创建人 + Object createBy = getFieldValByName("createBy", metaObject); + String username = SecurityUtils.getUsernameNoExcePtion(); + if (Objects.nonNull(username) && Objects.isNull(createBy)) { + setFieldValByName("updateBy", username, metaObject); + setFieldValByName("createBy", username, metaObject); + } + if (metaObject.hasGetter("updateTime") && metaObject.getValue("updateTime") == null) { + setFieldValByName("updateTime", new Date(), metaObject); + } + //强制设置租户字段 +// String tenantId = metaObject.findProperty("tenantId", true); +// if (StrUtil.isNotBlank(tenantId)) { +// setFieldValByName(tenantId, TenantContextHolder, metaObject); +// } +// System.out.println(1); + } + } + + @Override + public void updateFill(MetaObject metaObject) { + // 更新时间为空,则以当前时间为更新时间 + Object modifyTime = getFieldValByName("updateTime", metaObject); + if (Objects.isNull(modifyTime)) { + setFieldValByName("updateTime", new Date(), metaObject); + } + + // 当前登录用户不为空,更新人为空,则当前登录用户为更新人 + Object modifier = getFieldValByName("updater", metaObject); + Long userId = SecurityUtils.getLoginUserId(); + if (Objects.nonNull(userId) && Objects.isNull(modifier)) { + setFieldValByName("updater", userId.toString(), metaObject); + } + // 当前登录用户不为空,更新人为空,则当前登录用户为更新人 + Object updateBy = getFieldValByName("updateBy", metaObject); + String username = SecurityUtils.getUsernameNoExcePtion(); + if (Objects.nonNull(username) && Objects.isNull(updateBy)) { + setFieldValByName("updateBy", username, metaObject); + } + //强制设置租户字段 + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/mybatis/mapper/BaseMapperX.java b/ruoyi-common/src/main/java/com/ruoyi/common/mybatis/mapper/BaseMapperX.java new file mode 100644 index 00000000..667af281 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/mybatis/mapper/BaseMapperX.java @@ -0,0 +1,94 @@ +package com.ruoyi.common.mybatis.mapper; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +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.support.SFunction; +import com.ruoyi.common.mybatis.pojo.PageParam; +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.common.mybatis.util.MyBatisUtils; +import org.apache.ibatis.annotations.Param; + +import java.util.Collection; +import java.util.List; + +/** + * 在 MyBatis Plus 的 BaseMapper 的基础上拓展,提供更多的能力 + */ +public interface BaseMapperX extends BaseMapper { + + default PageResult selectPage(PageParam pageParam, @Param("ew") Wrapper queryWrapper) { + // MyBatis Plus 查询 + IPage mpPage = MyBatisUtils.buildPage(pageParam); + selectPage(mpPage, queryWrapper); + // 转换返回 + return new PageResult<>(mpPage.getRecords(), mpPage.getTotal()); + } + + default T selectOne(String field, Object value) { + return selectOne(new QueryWrapper().eq(field, value)); + } + + default T selectOne(SFunction field, Object value) { + return selectOne(new LambdaQueryWrapper().eq(field, value)); + } + + default T selectOne(String field1, Object value1, String field2, Object value2) { + return selectOne(new QueryWrapper().eq(field1, value1).eq(field2, value2)); + } + + default T selectOne(SFunction field1, Object value1, SFunction field2, Object value2) { + return selectOne(new LambdaQueryWrapper().eq(field1, value1).eq(field2, value2)); + } + + default Long selectCount() { + return selectCount(new QueryWrapper()); + } + + default Long selectCount(String field, Object value) { + return selectCount(new QueryWrapper().eq(field, value)); + } + + default Long selectCount(SFunction field, Object value) { + return selectCount(new LambdaQueryWrapper().eq(field, value)); + } + + default List selectList() { + return selectList(new QueryWrapper<>()); + } + + default List selectList(String field, Object value) { + return selectList(new QueryWrapper().eq(field, value)); + } + + default List selectList(SFunction field, Object value) { + return selectList(new LambdaQueryWrapper().eq(field, value)); + } + + default List selectList(String field, Collection values) { + return selectList(new QueryWrapper().in(field, values)); + } + + default List selectList(SFunction field, Collection values) { + return selectList(new LambdaQueryWrapper().in(field, values)); + } + + /** + * 逐条插入,适合少量数据插入,或者对性能要求不高的场景 + * + * 如果大量,请使用 {@link com.baomidou.mybatisplus.extension.service.impl.ServiceImpl#saveBatch(Collection)} 方法 + * 使用示例,可见 RoleMenuBatchInsertMapper、UserRoleBatchInsertMapper 类 + * + * @param entities 实体们 + */ + default void insertBatch(Collection entities) { + entities.forEach(this::insert); + } + + default void updateBatch(T update) { + update(update, new QueryWrapper<>()); + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/mybatis/pojo/PageParam.java b/ruoyi-common/src/main/java/com/ruoyi/common/mybatis/pojo/PageParam.java new file mode 100644 index 00000000..0d8df561 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/mybatis/pojo/PageParam.java @@ -0,0 +1,30 @@ +package com.ruoyi.common.mybatis.pojo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import java.io.Serializable; + +@ApiModel("分页参数") +@Data +public class PageParam implements Serializable { + + private static final Integer PAGE_NO = 1; + private static final Integer PAGE_SIZE = 10; + + @ApiModelProperty(value = "页码,从 1 开始", required = true,example = "1") + @NotNull(message = "页码不能为空") + @Min(value = 1, message = "页码最小值为 1") + private Integer pageNo = PAGE_NO; + + @ApiModelProperty(value = "每页条数,最大值为 100", required = true, example = "10") + @NotNull(message = "每页条数不能为空") + @Min(value = 1, message = "每页条数最小值为 1") + @Max(value = 100, message = "每页条数最大值为 100") + private Integer pageSize = PAGE_SIZE; + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/mybatis/pojo/PageResult.java b/ruoyi-common/src/main/java/com/ruoyi/common/mybatis/pojo/PageResult.java new file mode 100644 index 00000000..a20fb18f --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/mybatis/pojo/PageResult.java @@ -0,0 +1,42 @@ +package com.ruoyi.common.mybatis.pojo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +@ApiModel("分页结果") +@Data +public final class PageResult implements Serializable { + + @ApiModelProperty(value = "数据", required = true) + private List list; + + @ApiModelProperty(value = "总量", required = true) + private Long total; + + public PageResult() { + } + + public PageResult(List list, Long total) { + this.list = list; + this.total = total; + } + + public PageResult(Long total) { + this.list = new ArrayList<>(); + this.total = total; + } + + public static PageResult empty() { + return new PageResult<>(0L); + } + + public static PageResult empty(Long total) { + return new PageResult<>(total); + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/mybatis/pojo/SortingField.java b/ruoyi-common/src/main/java/com/ruoyi/common/mybatis/pojo/SortingField.java new file mode 100644 index 00000000..fbe472f8 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/mybatis/pojo/SortingField.java @@ -0,0 +1,56 @@ +package com.ruoyi.common.mybatis.pojo; + +import java.io.Serializable; + +/** + * 排序字段 DTO + * + * 类名加了 ing 的原因是,避免和 ES SortField 重名。 + */ +public class SortingField implements Serializable { + + /** + * 顺序 - 升序 + */ + public static final String ORDER_ASC = "asc"; + /** + * 顺序 - 降序 + */ + public static final String ORDER_DESC = "desc"; + + /** + * 字段 + */ + private String field; + /** + * 顺序 + */ + private String order; + + // 空构造方法,解决反序列化 + public SortingField() { + } + + public SortingField(String field, String order) { + this.field = field; + this.order = order; + } + + public String getField() { + return field; + } + + public SortingField setField(String field) { + this.field = field; + return this; + } + + public String getOrder() { + return order; + } + + public SortingField setOrder(String order) { + this.order = order; + return this; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/mybatis/query/LambdaQueryWrapperX.java b/ruoyi-common/src/main/java/com/ruoyi/common/mybatis/query/LambdaQueryWrapperX.java new file mode 100644 index 00000000..b6abbcac --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/mybatis/query/LambdaQueryWrapperX.java @@ -0,0 +1,135 @@ +package com.ruoyi.common.mybatis.query; + +import cn.hutool.core.util.ArrayUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import com.baomidou.mybatisplus.core.toolkit.support.SFunction; +import com.ruoyi.common.utils.collection.ArrayUtils; +import org.springframework.util.StringUtils; + +import java.util.Collection; + +/** + * 拓展 MyBatis Plus QueryWrapper 类,主要增加如下功能: + * + * 1. 拼接条件的方法,增加 xxxIfPresent 方法,用于判断值不存在的时候,不要拼接到条件中。 + * + * @param 数据类型 + */ +public class LambdaQueryWrapperX extends LambdaQueryWrapper { + + public LambdaQueryWrapperX likeIfPresent(SFunction column, String val) { + if (StringUtils.hasText(val)) { + return (LambdaQueryWrapperX) super.like(column, val); + } + return this; + } + + public LambdaQueryWrapperX inIfPresent(SFunction column, Collection values) { + if (!CollectionUtils.isEmpty(values)) { + return (LambdaQueryWrapperX) super.in(column, values); + } + return this; + } + + public LambdaQueryWrapperX inIfPresent(SFunction column, Object... values) { + if (!ArrayUtil.isEmpty(values)) { + return (LambdaQueryWrapperX) super.in(column, values); + } + return this; + } + + public LambdaQueryWrapperX eqIfPresent(SFunction column, Object val) { + if (val != null) { + return (LambdaQueryWrapperX) super.eq(column, val); + } + return this; + } + + public LambdaQueryWrapperX neIfPresent(SFunction column, Object val) { + if (val != null) { + return (LambdaQueryWrapperX) super.ne(column, val); + } + return this; + } + + public LambdaQueryWrapperX gtIfPresent(SFunction column, Object val) { + if (val != null) { + return (LambdaQueryWrapperX) super.gt(column, val); + } + return this; + } + + public LambdaQueryWrapperX geIfPresent(SFunction column, Object val) { + if (val != null) { + return (LambdaQueryWrapperX) super.ge(column, val); + } + return this; + } + + public LambdaQueryWrapperX ltIfPresent(SFunction column, Object val) { + if (val != null) { + return (LambdaQueryWrapperX) super.lt(column, val); + } + return this; + } + + public LambdaQueryWrapperX leIfPresent(SFunction column, Object val) { + if (val != null) { + return (LambdaQueryWrapperX) super.le(column, val); + } + return this; + } + + public LambdaQueryWrapperX betweenIfPresent(SFunction column, Object val1, Object val2) { + if (val1 != null && val2 != null) { + return (LambdaQueryWrapperX) super.between(column, val1, val2); + } + if (val1 != null) { + return (LambdaQueryWrapperX) ge(column, val1); + } + if (val2 != null) { + return (LambdaQueryWrapperX) le(column, val2); + } + return this; + } + + public LambdaQueryWrapperX betweenIfPresent(SFunction column, Object[] values) { + Object val1 = ArrayUtils.get(values, 0); + Object val2 = ArrayUtils.get(values, 1); + return betweenIfPresent(column, val1, val2); + } + + // ========== 重写父类方法,方便链式调用 ========== + + @Override + public LambdaQueryWrapperX eq(boolean condition, SFunction column, Object val) { + super.eq(condition, column, val); + return this; + } + + @Override + public LambdaQueryWrapperX eq(SFunction column, Object val) { + super.eq(column, val); + return this; + } + + @Override + public LambdaQueryWrapperX orderByDesc(SFunction column) { + super.orderByDesc(true, column); + return this; + } + + @Override + public LambdaQueryWrapperX last(String lastSql) { + super.last(lastSql); + return this; + } + + @Override + public LambdaQueryWrapperX in(SFunction column, Collection coll) { + super.in(column, coll); + return this; + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/mybatis/query/QueryWrapperX.java b/ruoyi-common/src/main/java/com/ruoyi/common/mybatis/query/QueryWrapperX.java new file mode 100644 index 00000000..a496f0a6 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/mybatis/query/QueryWrapperX.java @@ -0,0 +1,140 @@ +package com.ruoyi.common.mybatis.query; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.ArrayUtils; +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import org.springframework.util.StringUtils; + +import java.util.Collection; + +/** + * 拓展 MyBatis Plus QueryWrapper 类,主要增加如下功能: + * + * 1. 拼接条件的方法,增加 xxxIfPresent 方法,用于判断值不存在的时候,不要拼接到条件中。 + * + * @param 数据类型 + */ +public class QueryWrapperX extends QueryWrapper { + + public QueryWrapperX likeIfPresent(String column, String val) { + if (StringUtils.hasText(val)) { + return (QueryWrapperX) super.like(column, val); + } + return this; + } + + public QueryWrapperX inIfPresent(String column, Collection values) { + if (!CollectionUtils.isEmpty(values)) { + return (QueryWrapperX) super.in(column, values); + } + return this; + } + + public QueryWrapperX inIfPresent(String column, Object... values) { + if (!ArrayUtils.isEmpty(values)) { + return (QueryWrapperX) super.in(column, values); + } + return this; + } + + public QueryWrapperX eqIfPresent(String column, Object val) { + if (val != null) { + return (QueryWrapperX) super.eq(column, val); + } + return this; + } + + public QueryWrapperX neIfPresent(String column, Object val) { + if (val != null) { + return (QueryWrapperX) super.ne(column, val); + } + return this; + } + + public QueryWrapperX gtIfPresent(String column, Object val) { + if (val != null) { + return (QueryWrapperX) super.gt(column, val); + } + return this; + } + + public QueryWrapperX geIfPresent(String column, Object val) { + if (val != null) { + return (QueryWrapperX) super.ge(column, val); + } + return this; + } + + public QueryWrapperX ltIfPresent(String column, Object val) { + if (val != null) { + return (QueryWrapperX) super.lt(column, val); + } + return this; + } + + public QueryWrapperX leIfPresent(String column, Object val) { + if (val != null) { + return (QueryWrapperX) super.le(column, val); + } + return this; + } + + public QueryWrapperX betweenIfPresent(String column, Object val1, Object val2) { + if (val1 != null && val2 != null) { + return (QueryWrapperX) super.between(column, val1, val2); + } + if (val1 != null) { + return (QueryWrapperX) ge(column, val1); + } + if (val2 != null) { + return (QueryWrapperX) le(column, val2); + } + return this; + } + + public QueryWrapperX betweenIfPresent(String column, Object[] values) { + if (values!= null && values.length != 0 && values[0] != null && values[1] != null) { + return (QueryWrapperX) super.between(column, values[0], values[1]); + } + if (values!= null && values.length != 0 && values[0] != null) { + return (QueryWrapperX) ge(column, values[0]); + } + if (values!= null && values.length != 0 && values[1] != null) { + return (QueryWrapperX) le(column, values[1]); + } + return this; + } + + // ========== 重写父类方法,方便链式调用 ========== + + @Override + public QueryWrapperX eq(boolean condition, String column, Object val) { + super.eq(condition, column, val); + return this; + } + + @Override + public QueryWrapperX eq(String column, Object val) { + super.eq(column, val); + return this; + } + + @Override + public QueryWrapperX orderByDesc(String column) { + super.orderByDesc(true, column); + return this; + } + + @Override + public QueryWrapperX last(String lastSql) { + super.last(lastSql); + return this; + } + + @Override + public QueryWrapperX in(String column, Collection coll) { + super.in(column, coll); + return this; + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/mybatis/util/JdbcUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/mybatis/util/JdbcUtils.java new file mode 100644 index 00000000..0b82336e --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/mybatis/util/JdbcUtils.java @@ -0,0 +1,42 @@ +package com.ruoyi.common.mybatis.util; + +import com.baomidou.mybatisplus.annotation.DbType; + +import java.sql.Connection; +import java.sql.DriverManager; + +/** + * JDBC 工具类 + * + * hasPermi + */ +public class JdbcUtils { + + /** + * 判断连接是否正确 + * + * @param url 数据源连接 + * @param username 账号 + * @param password 密码 + * @return 是否正确 + */ + public static boolean isConnectionOK(String url, String username, String password) { + try (Connection ignored = DriverManager.getConnection(url, username, password)) { + return true; + } catch (Exception ex) { + return false; + } + } + + /** + * 获得 URL 对应的 DB 类型 + * + * @param url URL + * @return DB 类型 + */ + public static DbType getDbType(String url) { + String name = com.alibaba.druid.util.JdbcUtils.getDbType(url, null); + return DbType.getDbType(name); + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/mybatis/util/MyBatisUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/mybatis/util/MyBatisUtils.java new file mode 100644 index 00000000..f1e91ad3 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/mybatis/util/MyBatisUtils.java @@ -0,0 +1,84 @@ +package com.ruoyi.common.mybatis.util; + +import cn.hutool.core.collection.CollectionUtil; +import com.baomidou.mybatisplus.core.metadata.OrderItem; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ruoyi.common.mybatis.pojo.PageParam; +import com.ruoyi.common.mybatis.pojo.SortingField; +import net.sf.jsqlparser.expression.Alias; +import net.sf.jsqlparser.schema.Column; +import net.sf.jsqlparser.schema.Table; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +/** + * MyBatis 工具类 + */ +public class MyBatisUtils { + + private static final String MYSQL_ESCAPE_CHARACTER = "`"; + + public static Page buildPage(PageParam pageParam) { + return buildPage(pageParam, null); + } + + public static Page buildPage(PageParam pageParam, Collection sortingFields) { + // 页码 + 数量 + Page page = new Page<>(pageParam.getPageNo(), pageParam.getPageSize()); + // 排序字段 + if (!CollectionUtil.isEmpty(sortingFields)) { + page.addOrder(sortingFields.stream().map(sortingField -> SortingField.ORDER_ASC.equals(sortingField.getOrder()) ? + OrderItem.asc(sortingField.getField()) : OrderItem.desc(sortingField.getField())) + .collect(Collectors.toList())); + } + return page; + } + + /** + * 将拦截器添加到链中 + * 由于 MybatisPlusInterceptor 不支持添加拦截器,所以只能全量设置 + * + * @param interceptor 链 + * @param inner 拦截器 + * @param index 位置 + */ + public static void addInterceptor(MybatisPlusInterceptor interceptor, InnerInterceptor inner, int index) { + List inners = new ArrayList<>(interceptor.getInterceptors()); + inners.add(index, inner); + interceptor.setInterceptors(inners); + } + + /** + * 获得 Table 对应的表名 + * + * 兼容 MySQL 转义表名 `t_xxx` + * + * @param table 表 + * @return 去除转移字符后的表名 + */ + public static String getTableName(Table table) { + String tableName = table.getName(); + if (tableName.startsWith(MYSQL_ESCAPE_CHARACTER) && tableName.endsWith(MYSQL_ESCAPE_CHARACTER)) { + tableName = tableName.substring(1, tableName.length() - 1); + } + return tableName; + } + + /** + * 构建 Column 对象 + * + * @param tableName 表名 + * @param tableAlias 别名 + * @param column 字段名 + * @return Column 对象 + */ + public static Column buildColumn(String tableName, Alias tableAlias, String column) { + return new Column(tableAlias != null ? tableAlias.getName() + "." + column : column); + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/Arith.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/Arith.java new file mode 100644 index 00000000..b6326c2b --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/Arith.java @@ -0,0 +1,114 @@ +package com.ruoyi.common.utils; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +/** + * 精确的浮点数运算 + * + * @author ruoyi + */ +public class Arith +{ + + /** 默认除法运算精度 */ + private static final int DEF_DIV_SCALE = 10; + + /** 这个类不能实例化 */ + private Arith() + { + } + + /** + * 提供精确的加法运算。 + * @param v1 被加数 + * @param v2 加数 + * @return 两个参数的和 + */ + public static double add(double v1, double v2) + { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.add(b2).doubleValue(); + } + + /** + * 提供精确的减法运算。 + * @param v1 被减数 + * @param v2 减数 + * @return 两个参数的差 + */ + public static double sub(double v1, double v2) + { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.subtract(b2).doubleValue(); + } + + /** + * 提供精确的乘法运算。 + * @param v1 被乘数 + * @param v2 乘数 + * @return 两个参数的积 + */ + public static double mul(double v1, double v2) + { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.multiply(b2).doubleValue(); + } + + /** + * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 + * 小数点以后10位,以后的数字四舍五入。 + * @param v1 被除数 + * @param v2 除数 + * @return 两个参数的商 + */ + public static double div(double v1, double v2) + { + return div(v1, v2, DEF_DIV_SCALE); + } + + /** + * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 + * 定精度,以后的数字四舍五入。 + * @param v1 被除数 + * @param v2 除数 + * @param scale 表示表示需要精确到小数点以后几位。 + * @return 两个参数的商 + */ + public static double div(double v1, double v2, int scale) + { + if (scale < 0) + { + throw new IllegalArgumentException( + "The scale must be a positive integer or zero"); + } + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + if (b1.compareTo(BigDecimal.ZERO) == 0) + { + return BigDecimal.ZERO.doubleValue(); + } + return b1.divide(b2, scale, RoundingMode.HALF_UP).doubleValue(); + } + + /** + * 提供精确的小数位四舍五入处理。 + * @param v 需要四舍五入的数字 + * @param scale 小数点后保留几位 + * @return 四舍五入后的结果 + */ + public static double round(double v, int scale) + { + if (scale < 0) + { + throw new IllegalArgumentException( + "The scale must be a positive integer or zero"); + } + BigDecimal b = new BigDecimal(Double.toString(v)); + BigDecimal one = BigDecimal.ONE; + return b.divide(one, scale, RoundingMode.HALF_UP).doubleValue(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java new file mode 100644 index 00000000..e677b849 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java @@ -0,0 +1,189 @@ +package com.ruoyi.common.utils; + +import java.lang.management.ManagementFactory; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.Date; +import org.apache.commons.lang3.time.DateFormatUtils; + +/** + * 时间工具类 + * + * @author ruoyi + */ +public class DateUtils extends org.apache.commons.lang3.time.DateUtils +{ + public static String YYYY = "yyyy"; + + public static String YYYY_MM = "yyyy-MM"; + + public static String YYYY_MM_DD = "yyyy-MM-dd"; + + public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; + + public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; + + private static String[] parsePatterns = { + "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", + "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", + "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"}; + + /** + * 获取当前Date型日期 + * + * @return Date() 当前日期 + */ + public static Date getNowDate() + { + return new Date(); + } + public static boolean isExpired(Date time) { + return System.currentTimeMillis() > time.getTime(); + } + /** + * 获取当前日期, 默认格式为yyyy-MM-dd + * + * @return String + */ + public static String getDate() + { + return dateTimeNow(YYYY_MM_DD); + } + + public static final String getTime() + { + return dateTimeNow(YYYY_MM_DD_HH_MM_SS); + } + + public static final String dateTimeNow() + { + return dateTimeNow(YYYYMMDDHHMMSS); + } + + public static final String dateTimeNow(final String format) + { + return parseDateToStr(format, new Date()); + } + + public static final String dateTime(final Date date) + { + return parseDateToStr(YYYY_MM_DD, date); + } + + public static final String parseDateToStr(final String format, final Date date) + { + return new SimpleDateFormat(format).format(date); + } + + public static final Date dateTime(final String format, final String ts) + { + try + { + return new SimpleDateFormat(format).parse(ts); + } + catch (ParseException e) + { + throw new RuntimeException(e); + } + } + + /** + * 日期路径 即年/月/日 如2018/08/08 + */ + public static final String datePath() + { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyy/MM/dd"); + } + + /** + * 日期路径 即年/月/日 如20180808 + */ + public static final String dateTime() + { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyyMMdd"); + } + + /** + * 日期型字符串转化为日期 格式 + */ + public static Date parseDate(Object str) + { + if (str == null) + { + return null; + } + try + { + return parseDate(str.toString(), parsePatterns); + } + catch (ParseException e) + { + return null; + } + } + + /** + * 获取服务器启动时间 + */ + public static Date getServerStartDate() + { + long time = ManagementFactory.getRuntimeMXBean().getStartTime(); + return new Date(time); + } + + /** + * 计算相差天数 + */ + public static int differentDaysByMillisecond(Date date1, Date date2) + { + return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24))); + } + + /** + * 计算两个时间差 + */ + public static String getDatePoor(Date endDate, Date nowDate) + { + long nd = 1000 * 24 * 60 * 60; + long nh = 1000 * 60 * 60; + long nm = 1000 * 60; + // long ns = 1000; + // 获得两个时间的毫秒时间差异 + long diff = endDate.getTime() - nowDate.getTime(); + // 计算差多少天 + long day = diff / nd; + // 计算差多少小时 + long hour = diff % nd / nh; + // 计算差多少分钟 + long min = diff % nd % nh / nm; + // 计算差多少秒//输出结果 + // long sec = diff % nd % nh % nm / ns; + return day + "天" + hour + "小时" + min + "分钟"; + } + + /** + * 增加 LocalDateTime ==> Date + */ + public static Date toDate(LocalDateTime temporalAccessor) + { + ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } + + /** + * 增加 LocalDate ==> Date + */ + public static Date toDate(LocalDate temporalAccessor) + { + LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0)); + ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java new file mode 100644 index 00000000..13b3a83d --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java @@ -0,0 +1,200 @@ +package com.ruoyi.common.utils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.json.JSONUtil; +import com.alibaba.fastjson2.JSONArray; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.core.domain.entity.SysDictData; +import com.ruoyi.common.utils.spring.SpringUtils; +import net.sf.ehcache.CacheManager; + +/** + * 字典工具类 + * + * @author ruoyi + */ +public class DictUtils +{ + /** + * 分隔符 + */ + public static final String SEPARATOR = ","; + + /** + * 设置字典缓存 + * + * @param key 参数键 + * @param dictDatas 字典数据列表 + */ + public static void setDictCache(String key, List dictDatas) + { + EhcacheUtil.put("dict",getCacheKey(key),dictDatas); +// SpringUtils.getBean(RedisCache.class).setCacheObject(getCacheKey(key), dictDatas); + } + + /** + * 获取字典缓存 + * + * @param key 参数键 + * @return dictDatas 字典数据列表 + */ + public static List getDictCache(String key) + { + Object dict = EhcacheUtil.get("dict", getCacheKey(key)); + if (dict != null) { + JSONArray arrayCache = JSONArray.parseArray(JSONUtil.toJsonStr(dict)); + if (CollUtil.isNotEmpty(arrayCache)) { + return arrayCache.toList(SysDictData.class); + } + } +// JSONArray arrayCache = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key)); +// if (StringUtils.isNotNull(arrayCache)) +// { +// return arrayCache.toList(SysDictData.class); +// } + return new ArrayList<>(); + } + + /** + * 根据字典类型和字典值获取字典标签 + * + * @param dictType 字典类型 + * @param dictValue 字典值 + * @return 字典标签 + */ + public static String getDictLabel(String dictType, String dictValue) + { + return getDictLabel(dictType, dictValue, SEPARATOR); + } + + /** + * 根据字典类型和字典标签获取字典值 + * + * @param dictType 字典类型 + * @param dictLabel 字典标签 + * @return 字典值 + */ + public static String getDictValue(String dictType, String dictLabel) + { + return getDictValue(dictType, dictLabel, SEPARATOR); + } + + /** + * 根据字典类型和字典值获取字典标签 + * + * @param dictType 字典类型 + * @param dictValue 字典值 + * @param separator 分隔符 + * @return 字典标签 + */ + public static String getDictLabel(String dictType, String dictValue, String separator) + { + StringBuilder propertyString = new StringBuilder(); + List datas = getDictCache(dictType); + + if (StringUtils.isNotNull(datas)) + { + if (StringUtils.containsAny(separator, dictValue)) + { + for (SysDictData dict : datas) + { + for (String value : dictValue.split(separator)) + { + if (value.equals(dict.getDictValue())) + { + propertyString.append(dict.getDictLabel()).append(separator); + break; + } + } + } + } + else + { + for (SysDictData dict : datas) + { + if (dictValue.equals(dict.getDictValue())) + { + return dict.getDictLabel(); + } + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 根据字典类型和字典标签获取字典值 + * + * @param dictType 字典类型 + * @param dictLabel 字典标签 + * @param separator 分隔符 + * @return 字典值 + */ + public static String getDictValue(String dictType, String dictLabel, String separator) + { + StringBuilder propertyString = new StringBuilder(); + List datas = getDictCache(dictType); + + if (StringUtils.containsAny(separator, dictLabel) && StringUtils.isNotEmpty(datas)) + { + for (SysDictData dict : datas) + { + for (String label : dictLabel.split(separator)) + { + if (label.equals(dict.getDictLabel())) + { + propertyString.append(dict.getDictValue()).append(separator); + break; + } + } + } + } + else + { + for (SysDictData dict : datas) + { + if (dictLabel.equals(dict.getDictLabel())) + { + return dict.getDictValue(); + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 删除指定字典缓存 + * + * @param key 字典键 + */ + public static void removeDictCache(String key) + { + EhcacheUtil.remove("dict", getCacheKey(key)); +// SpringUtils.getBean(RedisCache.class).deleteObject(getCacheKey(key)); + } + + /** + * 清空字典缓存 + */ + public static void clearDictCache() + { + EhcacheUtil.cacheManager.getCache("dict").removeAll(); +// Collection keys = SpringUtils.getBean(RedisCache.class).keys(CacheConstants.SYS_DICT_KEY + "*"); +// SpringUtils.getBean(RedisCache.class).deleteObject(keys); + } + + /** + * 设置cache key + * + * @param configKey 参数键 + * @return 缓存键key + */ + public static String getCacheKey(String configKey) + { + return CacheConstants.SYS_DICT_KEY + configKey; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/EhcacheUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/EhcacheUtil.java new file mode 100644 index 00000000..9e622c0b --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/EhcacheUtil.java @@ -0,0 +1,78 @@ +package com.ruoyi.common.utils; + +import net.sf.ehcache.Cache; +import net.sf.ehcache.CacheManager; +import net.sf.ehcache.Element; + +import java.util.List; + +public class EhcacheUtil { + public static CacheManager cacheManager = CacheManager.create(); + /** + * 根据key值从缓存得到value + * @param cacheName + * @param key + * @return + */ + public static Object get(String cacheName, Object key) { + Cache cache = cacheManager.getCache(cacheName); + if(cache!= null) { + Element element = cache.get(key); + if(element != null) { + return element.getObjectValue(); + } + } + return null; + } + + /** + * 把对应key和vlaue数据存储到缓存 + * @param cacheName + * @param key + * @param value + */ + public static void put(String cacheName, Object key, Object value) { + Cache cache = cacheManager.getCache(cacheName); + if (cache != null) { + Element element = new Element(key, value); + cache.put(element); + cache.flush(); + } + } + /** + * 把对应key和vlaue数据存储到缓存 + * @param cacheName + * @param key + * @param value + */ + public static void put(String cacheName, Object key, Object value,int timeToIdleSeconds) { + Cache cache = cacheManager.getCache(cacheName); + if (cache != null) { + Element element = new Element(key, value); + element.setTimeToIdle(timeToIdleSeconds); + cache.put(element); + cache.flush(); + } + } + /** + * 根据k移除涉及权限的value + * @param key + * @return + */ + public static boolean remove(String cacheName, Object key) { + Cache cache = cacheManager.getCache(cacheName); + if (cache != null) { + return cache.remove(key); + } + return false; + } + + public static void removeAll(String cacheName, List keys) { + Cache cache = cacheManager.getCache(cacheName); + cache.removeAll(keys); + } + public static void removeAll(String cacheName) { + Cache cache = cacheManager.getCache(cacheName); + cache.removeAll(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ExcelUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ExcelUtils.java new file mode 100644 index 00000000..0e347615 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ExcelUtils.java @@ -0,0 +1,48 @@ +package com.ruoyi.common.utils; + +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.net.URLEncoder; +import java.util.List; + +/** + * Excel 工具类 + * + * @author 芋道源码 + */ +public class ExcelUtils { + + /** + * 将列表以 Excel 响应给前端 + * + * @param response 响应 + * @param filename 文件名 + * @param sheetName Excel sheet 名 + * @param head Excel head 头 + * @param data 数据列表哦 + * @param 泛型,保证 head 和 data 类型的一致性 + * @throws IOException 写入失败的情况 + */ + public static void write(HttpServletResponse response, String filename, String sheetName, + Class head, List data) throws IOException { + // 输出 Excel + EasyExcel.write(response.getOutputStream(), head) + .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理 + .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 基于 column 长度,自动适配。最大 255 宽度 + .sheet(sheetName).doWrite(data); + // 设置 header 和 contentType。写在最后的原因是,避免报错时,响应 contentType 已经被修改了 + response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8")); + response.setContentType("application/vnd.ms-excel;charset=UTF-8"); + } + + public static List read(MultipartFile file, Class head) throws IOException { + return EasyExcel.read(file.getInputStream(), head, null) + .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理 + .doReadAllSync(); + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java new file mode 100644 index 00000000..214e4a0d --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java @@ -0,0 +1,39 @@ +package com.ruoyi.common.utils; + +import java.io.PrintWriter; +import java.io.StringWriter; +import org.apache.commons.lang3.exception.ExceptionUtils; + +/** + * 错误信息处理类。 + * + * @author ruoyi + */ +public class ExceptionUtil +{ + /** + * 获取exception的详细错误信息。 + */ + public static String getExceptionMessage(Throwable e) + { + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw, true)); + return sw.toString(); + } + + public static String getRootErrorMessage(Exception e) + { + Throwable root = ExceptionUtils.getRootCause(e); + root = (root == null ? e : root); + if (root == null) + { + return ""; + } + String msg = root.getMessage(); + if (msg == null) + { + return "null"; + } + return StringUtils.defaultString(msg); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/JsonUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/JsonUtils.java new file mode 100644 index 00000000..e26dd4ab --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/JsonUtils.java @@ -0,0 +1,142 @@ +package com.ruoyi.common.utils; + +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import lombok.SneakyThrows; +import lombok.experimental.UtilityClass; +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * JSON 工具类 + * + * hasPermi + */ +@UtilityClass +@Slf4j +public class JsonUtils { + + private static ObjectMapper objectMapper = new ObjectMapper(); + + static { + objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + } + + /** + * 初始化 objectMapper 属性 + *

+ * 通过这样的方式,使用 Spring 创建的 ObjectMapper Bean + * + * @param objectMapper ObjectMapper 对象 + */ + public static void init(ObjectMapper objectMapper) { + JsonUtils.objectMapper = objectMapper; + } + + @SneakyThrows + public static String toJsonString(Object object) { + return objectMapper.writeValueAsString(object); + } + + @SneakyThrows + public static byte[] toJsonByte(Object object) { + return objectMapper.writeValueAsBytes(object); + } + + @SneakyThrows + public static String toJsonPrettyString(Object object) { + return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(object); + } + + public static T parseObject(String text, Class clazz) { + if (StrUtil.isEmpty(text)) { + return null; + } + try { + return objectMapper.readValue(text, clazz); + } catch (IOException e) { + log.error("json parse err,json:{}", text, e); + throw new RuntimeException(e); + } + } + + /** + * 将字符串解析成指定类型的对象 + * 使用 {@link #parseObject(String, Class)} 时,在@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) 的场景下, + * 如果 text 没有 class 属性,则会报错。此时,使用这个方法,可以解决。 + * + * @param text 字符串 + * @param clazz 类型 + * @return 对象 + */ + public static T parseObject2(String text, Class clazz) { + if (StrUtil.isEmpty(text)) { + return null; + } + return JSONUtil.toBean(text, clazz); + } + + public static T parseObject(byte[] bytes, Class clazz) { + if (ArrayUtil.isEmpty(bytes)) { + return null; + } + try { + return objectMapper.readValue(bytes, clazz); + } catch (IOException e) { + log.error("json parse err,json:{}", bytes, e); + throw new RuntimeException(e); + } + } + + public static T parseObject(String text, TypeReference typeReference) { + try { + return objectMapper.readValue(text, typeReference); + } catch (IOException e) { + log.error("json parse err,json:{}", text, e); + throw new RuntimeException(e); + } + } + + public static List parseArray(String text, Class clazz) { + if (StrUtil.isEmpty(text)) { + return new ArrayList<>(); + } + try { + return objectMapper.readValue(text, objectMapper.getTypeFactory().constructCollectionType(List.class, clazz)); + } catch (IOException e) { + log.error("json parse err,json:{}", text, e); + throw new RuntimeException(e); + } + } + + public static JsonNode parseTree(String text) { + try { + return objectMapper.readTree(text); + } catch (IOException e) { + log.error("json parse err,json:{}", text, e); + throw new RuntimeException(e); + } + } + + public static JsonNode parseTree(byte[] text) { + try { + return objectMapper.readTree(text); + } catch (IOException e) { + log.error("json parse err,json:{}", text, e); + throw new RuntimeException(e); + } + } + + public static boolean isJson(String text) { + return JSONUtil.isTypeJSON(text); + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/LogUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/LogUtils.java new file mode 100644 index 00000000..0de30c6b --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/LogUtils.java @@ -0,0 +1,18 @@ +package com.ruoyi.common.utils; + +/** + * 处理并记录日志文件 + * + * @author ruoyi + */ +public class LogUtils +{ + public static String getBlock(Object msg) + { + if (msg == null) + { + msg = ""; + } + return "[" + msg.toString() + "]"; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/MessageUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/MessageUtils.java new file mode 100644 index 00000000..7dac75a3 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/MessageUtils.java @@ -0,0 +1,26 @@ +package com.ruoyi.common.utils; + +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; +import com.ruoyi.common.utils.spring.SpringUtils; + +/** + * 获取i18n资源文件 + * + * @author ruoyi + */ +public class MessageUtils +{ + /** + * 根据消息键和参数 获取消息 委托给spring messageSource + * + * @param code 消息键 + * @param args 参数 + * @return 获取国际化翻译值 + */ + public static String message(String code, Object... args) + { + MessageSource messageSource = SpringUtils.getBean(MessageSource.class); + return messageSource.getMessage(code, args, LocaleContextHolder.getLocale()); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/NumberUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/NumberUtils.java new file mode 100644 index 00000000..adfebce1 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/NumberUtils.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.utils; + +import cn.hutool.core.util.StrUtil; + +/** + * 数字的工具类,补全 {@link cn.hutool.core.util.NumberUtil} 的功能 + * + * hasPermi + */ +public class NumberUtils { + + public static Long parseLong(String str) { + return StrUtil.isNotEmpty(str) ? Long.valueOf(str) : null; + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ObjectUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ObjectUtils.java new file mode 100644 index 00000000..db321ef8 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ObjectUtils.java @@ -0,0 +1,61 @@ +package com.ruoyi.common.utils; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.ReflectUtil; + +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.function.Consumer; + +/** + * Object 工具类 + * + * hasPermi + */ +public class ObjectUtils { + + /** + * 复制对象,并忽略 Id 编号 + * + * @param object 被复制对象 + * @param consumer 消费者,可以二次编辑被复制对象 + * @return 复制后的对象 + */ + public static T cloneIgnoreId(T object, Consumer consumer) { + T result = ObjectUtil.clone(object); + // 忽略 id 编号 + Field field = ReflectUtil.getField(object.getClass(), "id"); + if (field != null) { + ReflectUtil.setFieldValue(result, field, null); + } + // 二次编辑 + if (result != null) { + consumer.accept(result); + } + return result; + } + + public static > T max(T obj1, T obj2) { + if (obj1 == null) { + return obj2; + } + if (obj2 == null) { + return obj1; + } + return obj1.compareTo(obj2) > 0 ? obj1 : obj2; + } + + public static T defaultIfNull(T... array) { + for (T item : array) { + if (item != null) { + return item; + } + } + return null; + } + + public static boolean equalsAny(T obj, T... array) { + return Arrays.asList(array).contains(obj); + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/PageUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/PageUtils.java new file mode 100644 index 00000000..74219f16 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/PageUtils.java @@ -0,0 +1,48 @@ +package com.ruoyi.common.utils; + +import com.github.pagehelper.PageHelper; +import com.ruoyi.common.core.page.PageDomain; +import com.ruoyi.common.core.page.TableSupport; +import com.ruoyi.common.mybatis.pojo.PageParam; +import com.ruoyi.common.utils.sql.SqlUtil; + +/** + * 分页工具类 + * + * @author ruoyi + */ +public class PageUtils extends PageHelper +{ + /** + * 设置请求分页数据 + */ + public static void startPage() + { + PageDomain pageDomain = TableSupport.buildPageRequest(); + Integer pageNum = pageDomain.getPageNum(); + Integer pageSize = pageDomain.getPageSize(); + String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy()); + Boolean reasonable = pageDomain.getReasonable(); + PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable); + } + public static void startPage(String orderBy) + { + PageDomain pageDomain = TableSupport.buildPageRequest(); + Integer pageNum = pageDomain.getPageNum(); + Integer pageSize = pageDomain.getPageSize(); + Boolean reasonable = pageDomain.getReasonable(); + PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable); + } + /** + * 清理分页的线程变量 + */ + public static void clearPage() + { + PageHelper.clearPage(); + } + + public static int getStart(PageParam pageParam) { + return (pageParam.getPageNo() - 1) * pageParam.getPageSize(); + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java new file mode 100644 index 00000000..cb4504c6 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java @@ -0,0 +1,165 @@ +package com.ruoyi.common.utils; + +import org.springframework.lang.Nullable; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.exception.ServiceException; + +import java.util.Optional; + +/** + * 安全服务工具类 + * + * @author ruoyi + */ +public class SecurityUtils +{ + /** + * 用户ID + **/ + public static Long getUserId() + { + try + { + return getLoginUser().getUserId(); + } + catch (Exception e) + { + throw new ServiceException("获取用户ID异常", HttpStatus.UNAUTHORIZED); + } + } + + /** + * 获取部门ID + **/ + public static Long getDeptId() + { + try + { + return getLoginUser().getDeptId(); + } + catch (Exception e) + { + throw new ServiceException("获取部门ID异常", HttpStatus.UNAUTHORIZED); + } + } + + /** + * 获取用户账户 + **/ + public static String getUsername() + { + try + { + return getLoginUser().getUsername(); + } + catch (Exception e) + { + throw new ServiceException("获取用户账户异常", HttpStatus.UNAUTHORIZED); + } + } + + /** + * 获取用户 + **/ + public static LoginUser getLoginUser() + { + try + { + return (LoginUser) getAuthentication().getPrincipal(); + } + catch (Exception e) + { + throw new ServiceException("获取用户信息异常", HttpStatus.UNAUTHORIZED); + } + } + + /** + * 获取Authentication + */ + public static Authentication getAuthentication() + { + return SecurityContextHolder.getContext().getAuthentication(); + } + + /** + * 生成BCryptPasswordEncoder密码 + * + * @param password 密码 + * @return 加密字符串 + */ + public static String encryptPassword(String password) + { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.encode(password); + } + + /** + * 判断密码是否相同 + * + * @param rawPassword 真实密码 + * @param encodedPassword 加密后字符 + * @return 结果 + */ + public static boolean matchesPassword(String rawPassword, String encodedPassword) + { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.matches(rawPassword, encodedPassword); + } + + /** + * 是否为管理员 + * + * @param userId 用户ID + * @return 结果 + */ + public static boolean isAdmin(Long userId) + { + return userId != null && 1L == userId; + } + + @Nullable + public static Long getLoginUserId() { + SecurityContext context = SecurityContextHolder.getContext(); + Long userId = Optional.ofNullable(context).map(a -> a.getAuthentication()) + .map(b ->{ + if(b.getPrincipal() instanceof LoginUser){ + return (LoginUser) b.getPrincipal(); + } + return null; + }).map(c -> c.getUserId()).orElse(null); + return userId; + } + @Nullable + public static String getUsernameNoExcePtion() { + SecurityContext context = SecurityContextHolder.getContext(); + String userNaem = Optional.ofNullable(context).map(a -> a.getAuthentication()) + .map(b ->{ + if(b.getPrincipal() instanceof LoginUser){ + return (LoginUser) b.getPrincipal(); + } + return null; + }).map(c -> c.getUsername()).orElse(null); + return userNaem; + } + + /** + * 获取用户 + **/ + @Nullable + public static LoginUser getLoginUserNoExcePtion() + { + SecurityContext securityContext = SecurityContextHolder.getContext(); + if (securityContext != null && securityContext.getAuthentication() != null && securityContext.getAuthentication().getPrincipal() !=null) { + //已登录直接取用户的租户ID。这样判断是因为未登录的接口就需要从请求头获取了 + if(securityContext.getAuthentication().getPrincipal() instanceof LoginUser){ + return (LoginUser) securityContext.getAuthentication().getPrincipal(); + } + } + return null; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java new file mode 100644 index 00000000..f708252c --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java @@ -0,0 +1,199 @@ +package com.ruoyi.common.utils; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import cn.hutool.extra.servlet.ServletUtil; +import org.springframework.http.MediaType; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.text.Convert; + +/** + * 客户端工具类 + * + * @author ruoyi + */ +public class ServletUtils +{ + /** + * 获取String参数 + */ + public static String getParameter(String name) + { + return getRequest().getParameter(name); + } + + /** + * 获取String参数 + */ + public static String getParameter(String name, String defaultValue) + { + return Convert.toStr(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt(String name) + { + return Convert.toInt(getRequest().getParameter(name)); + } + + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt(String name, Integer defaultValue) + { + return Convert.toInt(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取Boolean参数 + */ + public static Boolean getParameterToBool(String name) + { + return Convert.toBool(getRequest().getParameter(name)); + } + + /** + * 获取Boolean参数 + */ + public static Boolean getParameterToBool(String name, Boolean defaultValue) + { + return Convert.toBool(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取request + */ + public static HttpServletRequest getRequest() + { + return getRequestAttributes().getRequest(); + } + + /** + * 获取response + */ + public static HttpServletResponse getResponse() + { + return getRequestAttributes().getResponse(); + } + + /** + * 获取session + */ + public static HttpSession getSession() + { + return getRequest().getSession(); + } + + public static ServletRequestAttributes getRequestAttributes() + { + RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); + return (ServletRequestAttributes) attributes; + } + + /** + * 将字符串渲染到客户端 + * + * @param response 渲染对象 + * @param string 待渲染的字符串 + */ + public static void renderString(HttpServletResponse response, String string) + { + try + { + response.setStatus(200); + response.setContentType("application/json"); + response.setCharacterEncoding("utf-8"); + response.getWriter().print(string); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + /** + * 返回 JSON 字符串 + * + * @param response 响应 + * @param object 对象,会序列化成 JSON 字符串 + */ + @SuppressWarnings("deprecation") // 必须使用 APPLICATION_JSON_UTF8_VALUE,否则会乱码 + public static void writeJSON(HttpServletResponse response, Object object) { + String content = JsonUtils.toJsonString(object); + ServletUtil.write(response, content, MediaType.APPLICATION_JSON_UTF8_VALUE); + } + /** + * 是否是Ajax异步请求 + * + * @param request + */ + public static boolean isAjaxRequest(HttpServletRequest request) + { + String accept = request.getHeader("accept"); + if (accept != null && accept.contains("application/json")) + { + return true; + } + + String xRequestedWith = request.getHeader("X-Requested-With"); + if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) + { + return true; + } + + String uri = request.getRequestURI(); + if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml")) + { + return true; + } + + String ajax = request.getParameter("__ajax"); + return StringUtils.inStringIgnoreCase(ajax, "json", "xml"); + } + + /** + * 内容编码 + * + * @param str 内容 + * @return 编码后的内容 + */ + public static String urlEncode(String str) + { + try + { + return URLEncoder.encode(str, Constants.UTF8); + } + catch (UnsupportedEncodingException e) + { + return StringUtils.EMPTY; + } + } + + /** + * 内容解码 + * + * @param str 内容 + * @return 解码后的内容 + */ + public static String urlDecode(String str) + { + try + { + return URLDecoder.decode(str, Constants.UTF8); + } + catch (UnsupportedEncodingException e) + { + return StringUtils.EMPTY; + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java new file mode 100644 index 00000000..3fdb58ab --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java @@ -0,0 +1,609 @@ +package com.ruoyi.common.utils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.springframework.util.AntPathMatcher; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.text.StrFormatter; + +/** + * 字符串工具类 + * + * @author ruoyi + */ +public class StringUtils extends org.apache.commons.lang3.StringUtils +{ + /** 空字符串 */ + private static final String NULLSTR = ""; + + /** 下划线 */ + private static final char SEPARATOR = '_'; + + /** + * 获取参数不为空值 + * + * @param value defaultValue 要判断的value + * @return value 返回值 + */ + public static T nvl(T value, T defaultValue) + { + return value != null ? value : defaultValue; + } + + /** + * * 判断一个Collection是否为空, 包含List,Set,Queue + * + * @param coll 要判断的Collection + * @return true:为空 false:非空 + */ + public static boolean isEmpty(Collection coll) + { + return isNull(coll) || coll.isEmpty(); + } + + /** + * * 判断一个Collection是否非空,包含List,Set,Queue + * + * @param coll 要判断的Collection + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Collection coll) + { + return !isEmpty(coll); + } + + /** + * * 判断一个对象数组是否为空 + * + * @param objects 要判断的对象数组 + ** @return true:为空 false:非空 + */ + public static boolean isEmpty(Object[] objects) + { + return isNull(objects) || (objects.length == 0); + } + + /** + * * 判断一个对象数组是否非空 + * + * @param objects 要判断的对象数组 + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Object[] objects) + { + return !isEmpty(objects); + } + + /** + * * 判断一个Map是否为空 + * + * @param map 要判断的Map + * @return true:为空 false:非空 + */ + public static boolean isEmpty(Map map) + { + return isNull(map) || map.isEmpty(); + } + + /** + * * 判断一个Map是否为空 + * + * @param map 要判断的Map + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Map map) + { + return !isEmpty(map); + } + + /** + * * 判断一个字符串是否为空串 + * + * @param str String + * @return true:为空 false:非空 + */ + public static boolean isEmpty(String str) + { + return isNull(str) || NULLSTR.equals(str.trim()); + } + + /** + * * 判断一个字符串是否为非空串 + * + * @param str String + * @return true:非空串 false:空串 + */ + public static boolean isNotEmpty(String str) + { + return !isEmpty(str); + } + + /** + * * 判断一个对象是否为空 + * + * @param object Object + * @return true:为空 false:非空 + */ + public static boolean isNull(Object object) + { + return object == null; + } + + /** + * * 判断一个对象是否非空 + * + * @param object Object + * @return true:非空 false:空 + */ + public static boolean isNotNull(Object object) + { + return !isNull(object); + } + + /** + * * 判断一个对象是否是数组类型(Java基本型别的数组) + * + * @param object 对象 + * @return true:是数组 false:不是数组 + */ + public static boolean isArray(Object object) + { + return isNotNull(object) && object.getClass().isArray(); + } + + /** + * 去空格 + */ + public static String trim(String str) + { + return (str == null ? "" : str.trim()); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @return 结果 + */ + public static String substring(final String str, int start) + { + if (str == null) + { + return NULLSTR; + } + + if (start < 0) + { + start = str.length() + start; + } + + if (start < 0) + { + start = 0; + } + if (start > str.length()) + { + return NULLSTR; + } + + return str.substring(start); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @param end 结束 + * @return 结果 + */ + public static String substring(final String str, int start, int end) + { + if (str == null) + { + return NULLSTR; + } + + if (end < 0) + { + end = str.length() + end; + } + if (start < 0) + { + start = str.length() + start; + } + + if (end > str.length()) + { + end = str.length(); + } + + if (start > end) + { + return NULLSTR; + } + + if (start < 0) + { + start = 0; + } + if (end < 0) + { + end = 0; + } + + return str.substring(start, end); + } + + /** + * 格式化文本, {} 表示占位符
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ * + * @param template 文本模板,被替换的部分用 {} 表示 + * @param params 参数值 + * @return 格式化后的文本 + */ + public static String format(String template, Object... params) + { + if (isEmpty(params) || isEmpty(template)) + { + return template; + } + return StrFormatter.format(template, params); + } + + /** + * 是否为http(s)://开头 + * + * @param link 链接 + * @return 结果 + */ + public static boolean ishttp(String link) + { + return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS); + } + + /** + * 字符串转set + * + * @param str 字符串 + * @param sep 分隔符 + * @return set集合 + */ + public static final Set str2Set(String str, String sep) + { + return new HashSet(str2List(str, sep, true, false)); + } + + /** + * 字符串转list + * + * @param str 字符串 + * @param sep 分隔符 + * @param filterBlank 过滤纯空白 + * @param trim 去掉首尾空白 + * @return list集合 + */ + public static final List str2List(String str, String sep, boolean filterBlank, boolean trim) + { + List list = new ArrayList(); + if (StringUtils.isEmpty(str)) + { + return list; + } + + // 过滤空白字符串 + if (filterBlank && StringUtils.isBlank(str)) + { + return list; + } + String[] split = str.split(sep); + for (String string : split) + { + if (filterBlank && StringUtils.isBlank(string)) + { + continue; + } + if (trim) + { + string = string.trim(); + } + list.add(string); + } + + return list; + } + + /** + * 判断给定的set列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value + * + * @param collection 给定的集合 + * @param array 给定的数组 + * @return boolean 结果 + */ + public static boolean containsAny(Collection collection, String... array) + { + if (isEmpty(collection) || isEmpty(array)) + { + return false; + } + else + { + for (String str : array) + { + if (collection.contains(str)) + { + return true; + } + } + return false; + } + } + + /** + * 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写 + * + * @param cs 指定字符串 + * @param searchCharSequences 需要检查的字符串数组 + * @return 是否包含任意一个字符串 + */ + public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) + { + if (isEmpty(cs) || isEmpty(searchCharSequences)) + { + return false; + } + for (CharSequence testStr : searchCharSequences) + { + if (containsIgnoreCase(cs, testStr)) + { + return true; + } + } + return false; + } + + /** + * 驼峰转下划线命名 + */ + public static String toUnderScoreCase(String str) + { + if (str == null) + { + return null; + } + StringBuilder sb = new StringBuilder(); + // 前置字符是否大写 + boolean preCharIsUpperCase = true; + // 当前字符是否大写 + boolean curreCharIsUpperCase = true; + // 下一字符是否大写 + boolean nexteCharIsUpperCase = true; + for (int i = 0; i < str.length(); i++) + { + char c = str.charAt(i); + if (i > 0) + { + preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1)); + } + else + { + preCharIsUpperCase = false; + } + + curreCharIsUpperCase = Character.isUpperCase(c); + + if (i < (str.length() - 1)) + { + nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1)); + } + + if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) + { + sb.append(SEPARATOR); + } + else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) + { + sb.append(SEPARATOR); + } + sb.append(Character.toLowerCase(c)); + } + + return sb.toString(); + } + + /** + * 是否包含字符串 + * + * @param str 验证字符串 + * @param strs 字符串组 + * @return 包含返回true + */ + public static boolean inStringIgnoreCase(String str, String... strs) + { + if (str != null && strs != null) + { + for (String s : strs) + { + if (str.equalsIgnoreCase(trim(s))) + { + return true; + } + } + } + return false; + } + + /** + * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld + * + * @param name 转换前的下划线大写方式命名的字符串 + * @return 转换后的驼峰式命名的字符串 + */ + public static String convertToCamelCase(String name) + { + StringBuilder result = new StringBuilder(); + // 快速检查 + if (name == null || name.isEmpty()) + { + // 没必要转换 + return ""; + } + else if (!name.contains("_")) + { + // 不含下划线,仅将首字母大写 + return name.substring(0, 1).toUpperCase() + name.substring(1); + } + // 用下划线将原始字符串分割 + String[] camels = name.split("_"); + for (String camel : camels) + { + // 跳过原始字符串中开头、结尾的下换线或双重下划线 + if (camel.isEmpty()) + { + continue; + } + // 首字母大写 + result.append(camel.substring(0, 1).toUpperCase()); + result.append(camel.substring(1).toLowerCase()); + } + return result.toString(); + } + + /** + * 驼峰式命名法 例如:user_name->userName + */ + public static String toCamelCase(String s) + { + if (s == null) + { + return null; + } + s = s.toLowerCase(); + StringBuilder sb = new StringBuilder(s.length()); + boolean upperCase = false; + for (int i = 0; i < s.length(); i++) + { + char c = s.charAt(i); + + if (c == SEPARATOR) + { + upperCase = true; + } + else if (upperCase) + { + sb.append(Character.toUpperCase(c)); + upperCase = false; + } + else + { + sb.append(c); + } + } + return sb.toString(); + } + + /** + * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串 + * + * @param str 指定字符串 + * @param strs 需要检查的字符串数组 + * @return 是否匹配 + */ + public static boolean matches(String str, List strs) + { + if (isEmpty(str) || isEmpty(strs)) + { + return false; + } + for (String pattern : strs) + { + if (isMatch(pattern, str)) + { + return true; + } + } + return false; + } + + /** + * 判断url是否与规则配置: + * ? 表示单个字符; + * * 表示一层路径内的任意字符串,不可跨层级; + * ** 表示任意层路径; + * + * @param pattern 匹配规则 + * @param url 需要匹配的url + * @return + */ + public static boolean isMatch(String pattern, String url) + { + AntPathMatcher matcher = new AntPathMatcher(); + return matcher.match(pattern, url); + } + + @SuppressWarnings("unchecked") + public static T cast(Object obj) + { + return (T) obj; + } + + /** + * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。 + * + * @param num 数字对象 + * @param size 字符串指定长度 + * @return 返回数字的字符串格式,该字符串为指定长度。 + */ + public static final String padl(final Number num, final int size) + { + return padl(num.toString(), size, '0'); + } + + /** + * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。 + * + * @param s 原始字符串 + * @param size 字符串指定长度 + * @param c 用于补齐的字符 + * @return 返回指定长度的字符串,由原字符串左补齐或截取得到。 + */ + public static final String padl(final String s, final int size, final char c) + { + final StringBuilder sb = new StringBuilder(size); + if (s != null) + { + final int len = s.length(); + if (s.length() <= size) + { + for (int i = size - len; i > 0; i--) + { + sb.append(c); + } + sb.append(s); + } + else + { + return s.substring(len - size, len); + } + } + else + { + for (int i = size; i > 0; i--) + { + sb.append(c); + } + } + return sb.toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/Threads.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/Threads.java new file mode 100644 index 00000000..71fe6d52 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/Threads.java @@ -0,0 +1,99 @@ +package com.ruoyi.common.utils; + +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 线程相关工具类. + * + * @author ruoyi + */ +public class Threads +{ + private static final Logger logger = LoggerFactory.getLogger(Threads.class); + + /** + * sleep等待,单位为毫秒 + */ + public static void sleep(long milliseconds) + { + try + { + Thread.sleep(milliseconds); + } + catch (InterruptedException e) + { + return; + } + } + + /** + * 停止线程池 + * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务. + * 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数. + * 如果仍然超時,則強制退出. + * 另对在shutdown时线程本身被调用中断做了处理. + */ + public static void shutdownAndAwaitTermination(ExecutorService pool) + { + if (pool != null && !pool.isShutdown()) + { + pool.shutdown(); + try + { + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) + { + pool.shutdownNow(); + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) + { + logger.info("Pool did not terminate"); + } + } + } + catch (InterruptedException ie) + { + pool.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + } + + /** + * 打印线程异常信息 + */ + public static void printException(Runnable r, Throwable t) + { + if (t == null && r instanceof Future) + { + try + { + Future future = (Future) r; + if (future.isDone()) + { + future.get(); + } + } + catch (CancellationException ce) + { + t = ce; + } + catch (ExecutionException ee) + { + t = ee.getCause(); + } + catch (InterruptedException ie) + { + Thread.currentThread().interrupt(); + } + } + if (t != null) + { + logger.error(t.getMessage(), t); + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/TreeUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/TreeUtils.java new file mode 100644 index 00000000..d711a7f4 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/TreeUtils.java @@ -0,0 +1,177 @@ +package com.ruoyi.common.utils; + +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.TreeConstants; +import com.ruoyi.common.core.domain.DictTreeEntity; +import com.ruoyi.common.utils.spring.SpringUtils; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * 字典工具类 + * + * @author wangzongrun + */ +public class TreeUtils { + + /** + * 设置cache key + * + * @param entities 参数键 + * @return 缓存键key + */ + public List buildTree(List entities) { + + List returnList = new ArrayList<>(); + for (Iterator iterator = entities.iterator(); iterator.hasNext(); ) { + Object obj = iterator.next(); + DictTreeEntity entity = (DictTreeEntity) obj; + // 如果是顶级节点, 遍历该父节点的所有子节点 + if (TreeConstants.PARENT_ROOT_VALUE.equals(entity.getPid())) { + recursionFn(entities, obj); + returnList.add(obj); + } + } + if (returnList.isEmpty()) { + entities.forEach(item -> { + returnList.add(item); + }); + } + + return returnList; + } + + /** + * 递归列表 + */ + private void recursionFn(List list, Object node) { + DictTreeEntity treeEntity = (DictTreeEntity) node; + // 得到子节点列表, 并赋值给node节点 + List childList = getChildList(list, node); + treeEntity.setChildren(childList); + + // 递归遍历 + for (Object tChild : childList) { + if (hasChild(list, tChild)) { + recursionFn(list, tChild); + } + } + } + + /** + * 得到子节点列表 + */ + private List getChildList(List list, Object t) { + DictTreeEntity treeEntity = (DictTreeEntity) t; + List tlist = new ArrayList(); + Iterator it = list.iterator(); + while (it.hasNext()) { + Object obj = it.next(); + DictTreeEntity n = (DictTreeEntity) obj; + if (StringUtils.isNotNull(n.getPid()) && n.getPid().equals(treeEntity.getId())) { + tlist.add(obj); + } + } + return tlist; + } + + /** + * 判断是否有子节点 + */ + private boolean hasChild(List list, Object t) { + return getChildList(list, t).size() > 0 ? true : false; + } + + /** + * 获取下一个编码 + */ + public static String genNextLevelCode(String maxLevelCode, String parentLevelCode, String initCode, Integer step) { + String nextCode = ""; + if (StringUtils.isNotEmpty(maxLevelCode)) { + List levelCodeList = Stream.of(maxLevelCode.split("\\" + TreeConstants.LAYOUT_CODE_SEPARATOR)).collect(Collectors.toList()); + String lastLevelCode = null; + + if (levelCodeList.size() > 0) { + lastLevelCode = String.valueOf(Integer.valueOf(levelCodeList.get(levelCodeList.size() - 1)) + step); + } else { + lastLevelCode = String.valueOf(Integer.valueOf(maxLevelCode) + step); + } + nextCode = maxLevelCode.substring(0, maxLevelCode.length() - lastLevelCode.length()) + lastLevelCode; + + } else { + nextCode = (StringUtils.isEmpty(parentLevelCode) ? "" : parentLevelCode + TreeConstants.LAYOUT_CODE_SEPARATOR) + initCode; + } + return nextCode; + } + + /** + * 获取层次 + */ + public static Integer genLevelDepth(String levelCode) { + return levelCode.split("\\" + TreeConstants.LAYOUT_CODE_SEPARATOR).length; + } + + /** + * 获取排序 + */ + public static Integer genOrderNum(String levelCode) { + List levelCodeList = Stream.of(levelCode.split("\\" + TreeConstants.LAYOUT_CODE_SEPARATOR)).collect(Collectors.toList()); + if (levelCodeList.size() > 0) { + return Integer.valueOf(levelCodeList.get(levelCodeList.size() - 1)); + } else { + return Integer.valueOf(levelCode); + } + } + + /** + * 获取编码 + */ + public static String genCode(String levelCode) { + return levelCode.replace(TreeConstants.LAYOUT_CODE_SEPARATOR, ""); + } + + /** + * 获取编码 + */ + public static boolean isParentNode(String pLevelCode, String levelCode) { + Integer pLen = pLevelCode.length(); + Integer len = levelCode.length(); + + if (pLen >= len) { + return false; + } + + if (levelCode.startsWith(pLevelCode)) { + pLevelCode += TreeConstants.LAYOUT_CODE_SEPARATOR; + String temp = levelCode.replace(pLevelCode, ""); + String[] temps = temp.split("\\" + TreeConstants.LAYOUT_CODE_SEPARATOR); + + if (temps.length > 1) { + return false; + } + } + return true; + } + + /** + * 树形结构数据到redis缓存中 + */ + public static void initCacheTreeData(String key, List node) { + // 存储到redis + EhcacheUtil.put("tree",getCacheKey(key),node); +// SpringUtils.getBean(RedisCache.class).setCacheObject(getCacheKey(key), node); + } + + /** + * 设置cache key + * @param configKey 参数键 + * @return 缓存键key + */ + public static String getCacheKey(String configKey) { + return CacheConstants.TREE_DICT_KEY+configKey; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ValidationUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ValidationUtils.java new file mode 100644 index 00000000..108de1f8 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ValidationUtils.java @@ -0,0 +1,49 @@ +package com.ruoyi.common.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import org.springframework.util.StringUtils; + +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import javax.validation.Validator; +import java.util.Set; +import java.util.regex.Pattern; + +/** + * 校验工具类 + * + * hasPermi + */ +public class ValidationUtils { + + private static final Pattern PATTERN_URL = Pattern.compile("^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]"); + + private static final Pattern PATTERN_XML_NCNAME = Pattern.compile("[a-zA-Z_][\\-_.0-9_a-zA-Z$]*"); + + public static boolean isMobile(String mobile) { + if (StrUtil.length(mobile) != 11) { + return false; + } + // TODO 芋艿,后面完善手机校验 + return true; + } + + public static boolean isURL(String url) { + return StringUtils.hasText(url) + && PATTERN_URL.matcher(url).matches(); + } + + public static boolean isXmlNCName(String str) { + return StringUtils.hasText(str) + && PATTERN_XML_NCNAME.matcher(str).matches(); + } + + public static void validate(Validator validator, Object object, Class... groups) { + Set> constraintViolations = validator.validate(object, groups); + if (CollUtil.isNotEmpty(constraintViolations)) { + throw new ConstraintViolationException(constraintViolations); + } + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanUtils.java new file mode 100644 index 00000000..4463662d --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanUtils.java @@ -0,0 +1,110 @@ +package com.ruoyi.common.utils.bean; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Bean 工具类 + * + * @author ruoyi + */ +public class BeanUtils extends org.springframework.beans.BeanUtils +{ + /** Bean方法名中属性名开始的下标 */ + private static final int BEAN_METHOD_PROP_INDEX = 3; + + /** * 匹配getter方法的正则表达式 */ + private static final Pattern GET_PATTERN = Pattern.compile("get(\\p{javaUpperCase}\\w*)"); + + /** * 匹配setter方法的正则表达式 */ + private static final Pattern SET_PATTERN = Pattern.compile("set(\\p{javaUpperCase}\\w*)"); + + /** + * Bean属性复制工具方法。 + * + * @param dest 目标对象 + * @param src 源对象 + */ + public static void copyBeanProp(Object dest, Object src) + { + try + { + copyProperties(src, dest); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + /** + * 获取对象的setter方法。 + * + * @param obj 对象 + * @return 对象的setter方法列表 + */ + public static List getSetterMethods(Object obj) + { + // setter方法列表 + List setterMethods = new ArrayList(); + + // 获取所有方法 + Method[] methods = obj.getClass().getMethods(); + + // 查找setter方法 + + for (Method method : methods) + { + Matcher m = SET_PATTERN.matcher(method.getName()); + if (m.matches() && (method.getParameterTypes().length == 1)) + { + setterMethods.add(method); + } + } + // 返回setter方法列表 + return setterMethods; + } + + /** + * 获取对象的getter方法。 + * + * @param obj 对象 + * @return 对象的getter方法列表 + */ + + public static List getGetterMethods(Object obj) + { + // getter方法列表 + List getterMethods = new ArrayList(); + // 获取所有方法 + Method[] methods = obj.getClass().getMethods(); + // 查找getter方法 + for (Method method : methods) + { + Matcher m = GET_PATTERN.matcher(method.getName()); + if (m.matches() && (method.getParameterTypes().length == 0)) + { + getterMethods.add(method); + } + } + // 返回getter方法列表 + return getterMethods; + } + + /** + * 检查Bean方法名中的属性名是否相等。
+ * 如getName()和setName()属性名一样,getName()和setAge()属性名不一样。 + * + * @param m1 方法名1 + * @param m2 方法名2 + * @return 属性名一样返回true,否则返回false + */ + + public static boolean isMethodPropEquals(String m1, String m2) + { + return m1.substring(BEAN_METHOD_PROP_INDEX).equals(m2.substring(BEAN_METHOD_PROP_INDEX)); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanValidators.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanValidators.java new file mode 100644 index 00000000..80bfed7b --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanValidators.java @@ -0,0 +1,24 @@ +package com.ruoyi.common.utils.bean; + +import java.util.Set; +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import javax.validation.Validator; + +/** + * bean对象属性验证 + * + * @author ruoyi + */ +public class BeanValidators +{ + public static void validateWithException(Validator validator, Object object, Class... groups) + throws ConstraintViolationException + { + Set> constraintViolations = validator.validate(object, groups); + if (!constraintViolations.isEmpty()) + { + throw new ConstraintViolationException(constraintViolations); + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/collection/ArrayUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/collection/ArrayUtils.java new file mode 100644 index 00000000..798b819a --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/collection/ArrayUtils.java @@ -0,0 +1,59 @@ +package com.ruoyi.common.utils.collection; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.collection.IterUtil; +import cn.hutool.core.util.ArrayUtil; + +import java.util.Collection; +import java.util.function.Consumer; +import java.util.function.Function; + +import static com.ruoyi.common.utils.collection.CollectionUtils.convertList; + + +/** + * Array 工具类 + *

+ * hasPermi + */ +public class ArrayUtils { + + /** + * 将 object 和 newElements 合并成一个数组 + * + * @param object 对象 + * @param newElements 数组 + * @param 泛型 + * @return 结果数组 + */ + @SafeVarargs + public static Consumer[] append(Consumer object, Consumer... newElements) { + if (object == null) { + return newElements; + } + Consumer[] result = ArrayUtil.newArray(Consumer.class, 1 + newElements.length); + result[0] = object; + System.arraycopy(newElements, 0, result, 1, newElements.length); + return result; + } + + public static V[] toArray(Collection from, Function mapper) { + return toArray(convertList(from, mapper)); + } + + @SuppressWarnings("unchecked") + public static T[] toArray(Collection from) { + if (CollectionUtil.isEmpty(from)) { + return (T[]) (new Object[0]); + } + return ArrayUtil.toArray(from, (Class) IterUtil.getElementType(from.iterator())); + } + + public static T get(T[] array, int index) { + if (null == array || index >= array.length) { + return null; + } + return array[index]; + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/collection/CollectionUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/collection/CollectionUtils.java new file mode 100644 index 00000000..5a2af66f --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/collection/CollectionUtils.java @@ -0,0 +1,187 @@ +package com.ruoyi.common.utils.collection; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; +import com.google.common.collect.ImmutableMap; + +import java.util.*; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +/** + * Collection 工具类 + * + * hasPermi + */ +public class CollectionUtils { + + public static boolean containsAny(Object source, Object... targets) { + return Arrays.asList(targets).contains(source); + } + + public static boolean isAnyEmpty(Collection... collections) { + return Arrays.stream(collections).anyMatch(CollectionUtil::isEmpty); + } + + public static List filterList(Collection from, Predicate predicate) { + if (CollUtil.isEmpty(from)) { + return new ArrayList<>(); + } + return from.stream().filter(predicate).collect(Collectors.toList()); + } + + public static List distinct(Collection from, Function keyMapper) { + if (CollUtil.isEmpty(from)) { + return new ArrayList<>(); + } + return distinct(from, keyMapper, (t1, t2) -> t1); + } + + public static List distinct(Collection from, Function keyMapper, BinaryOperator cover) { + if (CollUtil.isEmpty(from)) { + return new ArrayList<>(); + } + return new ArrayList<>(convertMap(from, keyMapper, Function.identity(), cover).values()); + } + + public static List convertList(Collection from, Function func) { + if (CollUtil.isEmpty(from)) { + return new ArrayList<>(); + } + return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toList()); + } + + public static List convertList(Collection from, Function func, Predicate filter) { + if (CollUtil.isEmpty(from)) { + return new ArrayList<>(); + } + return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toList()); + } + + public static Set convertSet(Collection from, Function func) { + if (CollUtil.isEmpty(from)) { + return new HashSet<>(); + } + return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toSet()); + } + + public static Set convertSet(Collection from, Function func, Predicate filter) { + if (CollUtil.isEmpty(from)) { + return new HashSet<>(); + } + return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toSet()); + } + + public static Map convertMap(Collection from, Function keyFunc) { + if (CollUtil.isEmpty(from)) { + return new HashMap<>(); + } + return convertMap(from, keyFunc, Function.identity()); + } + + public static Map convertMap(Collection from, Function keyFunc, Supplier> supplier) { + if (CollUtil.isEmpty(from)) { + return supplier.get(); + } + return convertMap(from, keyFunc, Function.identity(), supplier); + } + + public static Map convertMap(Collection from, Function keyFunc, Function valueFunc) { + if (CollUtil.isEmpty(from)) { + return new HashMap<>(); + } + return convertMap(from, keyFunc, valueFunc, (v1, v2) -> v1); + } + + public static Map convertMap(Collection from, Function keyFunc, Function valueFunc, BinaryOperator mergeFunction) { + if (CollUtil.isEmpty(from)) { + return new HashMap<>(); + } + return convertMap(from, keyFunc, valueFunc, mergeFunction, HashMap::new); + } + + public static Map convertMap(Collection from, Function keyFunc, Function valueFunc, Supplier> supplier) { + if (CollUtil.isEmpty(from)) { + return supplier.get(); + } + return convertMap(from, keyFunc, valueFunc, (v1, v2) -> v1, supplier); + } + + public static Map convertMap(Collection from, Function keyFunc, Function valueFunc, BinaryOperator mergeFunction, Supplier> supplier) { + if (CollUtil.isEmpty(from)) { + return new HashMap<>(); + } + return from.stream().collect(Collectors.toMap(keyFunc, valueFunc, mergeFunction, supplier)); + } + + public static Map> convertMultiMap(Collection from, Function keyFunc) { + if (CollUtil.isEmpty(from)) { + return new HashMap<>(); + } + return from.stream().collect(Collectors.groupingBy(keyFunc, Collectors.mapping(t -> t, Collectors.toList()))); + } + + public static Map> convertMultiMap(Collection from, Function keyFunc, Function valueFunc) { + if (CollUtil.isEmpty(from)) { + return new HashMap<>(); + } + return from.stream() + .collect(Collectors.groupingBy(keyFunc, Collectors.mapping(valueFunc, Collectors.toList()))); + } + + // 暂时没想好名字,先以 2 结尾噶 + public static Map> convertMultiMap2(Collection from, Function keyFunc, Function valueFunc) { + if (CollUtil.isEmpty(from)) { + return new HashMap<>(); + } + return from.stream().collect(Collectors.groupingBy(keyFunc, Collectors.mapping(valueFunc, Collectors.toSet()))); + } + + public static Map convertImmutableMap(Collection from, Function keyFunc) { + if (CollUtil.isEmpty(from)) { + return Collections.emptyMap(); + } + ImmutableMap.Builder builder = ImmutableMap.builder(); + from.forEach(item -> builder.put(keyFunc.apply(item), item)); + return builder.build(); + } + + public static boolean containsAny(Collection source, Collection candidates) { + return org.springframework.util.CollectionUtils.containsAny(source, candidates); + } + + public static T getFirst(List from) { + return !CollectionUtil.isEmpty(from) ? from.get(0) : null; + } + + public static T findFirst(List from, Predicate predicate) { + if (CollUtil.isEmpty(from)) { + return null; + } + return from.stream().filter(predicate).findFirst().orElse(null); + } + + public static > V getMaxValue(List from, Function valueFunc) { + if (CollUtil.isEmpty(from)) { + return null; + } + assert from.size() > 0; // 断言,避免告警 + T t = from.stream().max(Comparator.comparing(valueFunc)).get(); + return valueFunc.apply(t); + } + + public static void addIfNotNull(Collection coll, T item) { + if (item == null) { + return; + } + coll.add(item); + } + + public static Collection singleton(T deptId) { + return deptId == null ? Collections.emptyList() : Collections.singleton(deptId); + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/collection/IntArrayValuable.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/collection/IntArrayValuable.java new file mode 100644 index 00000000..983198a5 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/collection/IntArrayValuable.java @@ -0,0 +1,15 @@ +package com.ruoyi.common.utils.collection; + +/** + * 可生成 Int 数组的接口 + * + * hasPermi + */ +public interface IntArrayValuable { + + /** + * @return int 数组 + */ + int[] array(); + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/collection/KeyValue.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/collection/KeyValue.java new file mode 100644 index 00000000..53fac33e --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/collection/KeyValue.java @@ -0,0 +1,20 @@ +package com.ruoyi.common.utils.collection; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Key Value 的键值对 + * + * hasPermi + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class KeyValue { + + private K key; + private V value; + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/collection/MapUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/collection/MapUtils.java new file mode 100644 index 00000000..c845214e --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/collection/MapUtils.java @@ -0,0 +1,65 @@ +package com.ruoyi.common.utils.collection; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +/** + * Map 工具类 + * + * hasPermi + */ +public class MapUtils { + + /** + * 从哈希表表中,获得 keys 对应的所有 value 数组 + * + * @param multimap 哈希表 + * @param keys keys + * @return value 数组 + */ + public static List getList(Multimap multimap, Collection keys) { + List result = new ArrayList<>(); + keys.forEach(k -> { + Collection values = multimap.get(k); + if (CollectionUtil.isEmpty(values)) { + return; + } + result.addAll(values); + }); + return result; + } + + /** + * 从哈希表查找到 key 对应的 value,然后进一步处理 + * 注意,如果查找到的 value 为 null 时,不进行处理 + * + * @param map 哈希表 + * @param key key + * @param consumer 进一步处理的逻辑 + */ + public static void findAndThen(Map map, K key, Consumer consumer) { + if (CollUtil.isEmpty(map)) { + return; + } + V value = map.get(key); + if (value == null) { + return; + } + consumer.accept(value); + } + + public static Map convertMap(List> keyValues) { + Map map = Maps.newLinkedHashMapWithExpectedSize(keyValues.size()); + keyValues.forEach(keyValue -> map.put(keyValue.getKey(), keyValue.getValue())); + return map; + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/collection/SetUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/collection/SetUtils.java new file mode 100644 index 00000000..27eea0c3 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/collection/SetUtils.java @@ -0,0 +1,18 @@ +package com.ruoyi.common.utils.collection; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * Set 工具类 + * + * hasPermi + */ +public class SetUtils { + + public static Set asSet(T... objs) { + return new HashSet<>(Arrays.asList(objs)); + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java new file mode 100644 index 00000000..68130b9c --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java @@ -0,0 +1,76 @@ +package com.ruoyi.common.utils.file; + +import java.io.File; +import org.apache.commons.lang3.StringUtils; + +/** + * 文件类型工具类 + * + * @author ruoyi + */ +public class FileTypeUtils +{ + /** + * 获取文件类型 + *

+ * 例如: ruoyi.txt, 返回: txt + * + * @param file 文件名 + * @return 后缀(不含".") + */ + public static String getFileType(File file) + { + if (null == file) + { + return StringUtils.EMPTY; + } + return getFileType(file.getName()); + } + + /** + * 获取文件类型 + *

+ * 例如: ruoyi.txt, 返回: txt + * + * @param fileName 文件名 + * @return 后缀(不含".") + */ + public static String getFileType(String fileName) + { + int separatorIndex = fileName.lastIndexOf("."); + if (separatorIndex < 0) + { + return ""; + } + return fileName.substring(separatorIndex + 1).toLowerCase(); + } + + /** + * 获取文件类型 + * + * @param photoByte 文件字节码 + * @return 后缀(不含".") + */ + public static String getFileExtendName(byte[] photoByte) + { + String strFileExtendName = "JPG"; + if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56) + && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) + { + strFileExtendName = "GIF"; + } + else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) + { + strFileExtendName = "JPG"; + } + else if ((photoByte[0] == 66) && (photoByte[1] == 77)) + { + strFileExtendName = "BMP"; + } + else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) + { + strFileExtendName = "PNG"; + } + return strFileExtendName; + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java new file mode 100644 index 00000000..d0bb68f6 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java @@ -0,0 +1,200 @@ +package com.ruoyi.common.utils.file; + +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.exception.file.FileNameLengthLimitExceededException; +import com.ruoyi.common.exception.file.FileSizeLimitExceededException; +import com.ruoyi.common.exception.file.InvalidExtensionException; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.uuid.Seq; +import org.apache.commons.io.FilenameUtils; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; +import java.util.Objects; + +/** + * 文件上传工具类 + * + * @author ruoyi + */ +public class FileUploadUtils { + /** + * 默认大小 50M + */ + public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024; + + /** + * 默认的文件名最大长度 100 + */ + public static final int DEFAULT_FILE_NAME_LENGTH = 100; + + /** + * 本地默认上传的地址 + */ + private static String defaultBaseDir = RuoYiConfig.getProfile(); + + + public static void setDefaultBaseDir(String defaultBaseDir) { + FileUploadUtils.defaultBaseDir = defaultBaseDir; + } + + public static String getDefaultBaseDir() { + return defaultBaseDir; + } + + + /** + * 以默认配置进行文件上传 + * + * @param file 上传的文件 + * @return 文件名称 + * @throws Exception + */ + public static final String upload(MultipartFile file) throws IOException { + try { + return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); + } catch (Exception e) { + throw new IOException(e.getMessage(), e); + } + } + + /** + * 根据文件路径上传 + * + * @param baseDir 相对应用的基目录 + * @param file 上传的文件 + * @return 文件名称 + * @throws IOException + */ + public static final String upload(String baseDir, MultipartFile file) throws IOException { + try { + return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); + } catch (Exception e) { + throw new IOException(e.getMessage(), e); + } + } + + /** + * 文件上传 + * + * @param baseDir 相对应用的基目录 + * @param file 上传的文件 + * @param allowedExtension 上传文件类型 + * @return 返回上传成功的文件名 + * @throws FileSizeLimitExceededException 如果超出最大大小 + * @throws FileNameLengthLimitExceededException 文件名太长 + * @throws IOException 比如读写文件出错时 + * @throws InvalidExtensionException 文件校验异常 + */ + public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension) + throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException, + InvalidExtensionException { + int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length(); + if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) { + throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH); + } + + assertAllowed(file, allowedExtension); + + String fileName = extractFilename(file); + + String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath(); + file.transferTo(Paths.get(absPath)); + return getPathFileName(baseDir, fileName); + } + + /** + * 编码文件名 + */ + public static final String extractFilename(MultipartFile file) { + return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(), + FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file)); + } + + public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException { + File desc = new File(uploadDir + File.separator + fileName); + + if (!desc.exists()) { + if (!desc.getParentFile().exists()) { + desc.getParentFile().mkdirs(); + } + } + return desc; + } + + public static final String getPathFileName(String uploadDir, String fileName) throws IOException { + int dirLastIndex = RuoYiConfig.getProfile().length() + 1; + String currentDir = StringUtils.substring(uploadDir, dirLastIndex); + return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName; + } + + /** + * 文件大小校验 + * + * @param file 上传的文件 + * @return + * @throws FileSizeLimitExceededException 如果超出最大大小 + * @throws InvalidExtensionException + */ + public static final void assertAllowed(MultipartFile file, String[] allowedExtension) + throws FileSizeLimitExceededException, InvalidExtensionException { + long size = file.getSize(); + if (size > DEFAULT_MAX_SIZE) { + throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024); + } + + String fileName = file.getOriginalFilename(); + String extension = getExtension(file); + if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) { + if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) { + throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension, + fileName); + } else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) { + throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension, + fileName); + } else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) { + throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension, + fileName); + } else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION) { + throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension, + fileName); + } else { + throw new InvalidExtensionException(allowedExtension, extension, fileName); + } + } + } + + /** + * 判断MIME类型是否是允许的MIME类型 + * + * @param extension + * @param allowedExtension + * @return + */ + public static final boolean isAllowedExtension(String extension, String[] allowedExtension) { + for (String str : allowedExtension) { + if (str.equalsIgnoreCase(extension)) { + return true; + } + } + return false; + } + + /** + * 获取文件名的后缀 + * + * @param file 表单文件 + * @return 后缀名 + */ + public static final String getExtension(MultipartFile file) { + String extension = FilenameUtils.getExtension(file.getOriginalFilename()); + if (StringUtils.isEmpty(extension)) { + extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType())); + } + return extension; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java new file mode 100644 index 00000000..f8442702 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java @@ -0,0 +1,293 @@ +package com.ruoyi.common.utils.file; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.ArrayUtils; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.uuid.IdUtils; +import org.apache.commons.io.FilenameUtils; + +/** + * 文件处理工具类 + * + * @author ruoyi + */ +public class FileUtils +{ + public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+"; + + /** + * 输出指定文件的byte数组 + * + * @param filePath 文件路径 + * @param os 输出流 + * @return + */ + public static void writeBytes(String filePath, OutputStream os) throws IOException + { + FileInputStream fis = null; + try + { + File file = new File(filePath); + if (!file.exists()) + { + throw new FileNotFoundException(filePath); + } + fis = new FileInputStream(file); + byte[] b = new byte[1024]; + int length; + while ((length = fis.read(b)) > 0) + { + os.write(b, 0, length); + } + } + catch (IOException e) + { + throw e; + } + finally + { + IOUtils.close(os); + IOUtils.close(fis); + } + } + + /** + * 写数据到文件中 + * + * @param data 数据 + * @return 目标文件 + * @throws IOException IO异常 + */ + public static String writeImportBytes(byte[] data) throws IOException + { + return writeBytes(data, RuoYiConfig.getImportPath()); + } + + /** + * 写数据到文件中 + * + * @param data 数据 + * @param uploadDir 目标文件 + * @return 目标文件 + * @throws IOException IO异常 + */ + public static String writeBytes(byte[] data, String uploadDir) throws IOException + { + FileOutputStream fos = null; + String pathName = ""; + try + { + String extension = getFileExtendName(data); + pathName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension; + File file = FileUploadUtils.getAbsoluteFile(uploadDir, pathName); + fos = new FileOutputStream(file); + fos.write(data); + } + finally + { + IOUtils.close(fos); + } + return FileUploadUtils.getPathFileName(uploadDir, pathName); + } + + /** + * 删除文件 + * + * @param filePath 文件 + * @return + */ + public static boolean deleteFile(String filePath) + { + boolean flag = false; + File file = new File(filePath); + // 路径为文件且不为空则进行删除 + if (file.isFile() && file.exists()) + { + file.delete(); + flag = true; + } + return flag; + } + + /** + * 文件名称验证 + * + * @param filename 文件名称 + * @return true 正常 false 非法 + */ + public static boolean isValidFilename(String filename) + { + return filename.matches(FILENAME_PATTERN); + } + + /** + * 检查文件是否可下载 + * + * @param resource 需要下载的文件 + * @return true 正常 false 非法 + */ + public static boolean checkAllowDownload(String resource) + { + // 禁止目录上跳级别 + if (StringUtils.contains(resource, "..")) + { + return false; + } + + // 检查允许下载的文件规则 + if (ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource))) + { + return true; + } + + // 不在允许下载的文件规则 + return false; + } + + /** + * 下载文件名重新编码 + * + * @param request 请求对象 + * @param fileName 文件名 + * @return 编码后的文件名 + */ + public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException + { + final String agent = request.getHeader("USER-AGENT"); + String filename = fileName; + if (agent.contains("MSIE")) + { + // IE浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + filename = filename.replace("+", " "); + } + else if (agent.contains("Firefox")) + { + // 火狐浏览器 + filename = new String(fileName.getBytes(), "ISO8859-1"); + } + else if (agent.contains("Chrome")) + { + // google浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + } + else + { + // 其它浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + } + return filename; + } + + /** + * 下载文件名重新编码 + * + * @param response 响应对象 + * @param realFileName 真实文件名 + */ + public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException + { + String percentEncodedFileName = percentEncode(realFileName); + + StringBuilder contentDispositionValue = new StringBuilder(); + contentDispositionValue.append("attachment; filename=") + .append(percentEncodedFileName) + .append(";") + .append("filename*=") + .append("utf-8''") + .append(percentEncodedFileName); + + response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename"); + response.setHeader("Content-disposition", contentDispositionValue.toString()); + response.setHeader("download-filename", percentEncodedFileName); + } + + /** + * 百分号编码工具方法 + * + * @param s 需要百分号编码的字符串 + * @return 百分号编码后的字符串 + */ + public static String percentEncode(String s) throws UnsupportedEncodingException + { + String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString()); + return encode.replaceAll("\\+", "%20"); + } + + /** + * 获取图像后缀 + * + * @param photoByte 图像数据 + * @return 后缀名 + */ + public static String getFileExtendName(byte[] photoByte) + { + String strFileExtendName = "jpg"; + if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56) + && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) + { + strFileExtendName = "gif"; + } + else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) + { + strFileExtendName = "jpg"; + } + else if ((photoByte[0] == 66) && (photoByte[1] == 77)) + { + strFileExtendName = "bmp"; + } + else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) + { + strFileExtendName = "png"; + } + return strFileExtendName; + } + + /** + * 获取文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi.png + * + * @param fileName 路径名称 + * @return 没有文件路径的名称 + */ + public static String getName(String fileName) + { + if (fileName == null) + { + return null; + } + int lastUnixPos = fileName.lastIndexOf('/'); + int lastWindowsPos = fileName.lastIndexOf('\\'); + int index = Math.max(lastUnixPos, lastWindowsPos); + return fileName.substring(index + 1); + } + + /** + * 获取不带后缀文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi + * + * @param fileName 路径名称 + * @return 没有文件路径和后缀的名称 + */ + public static String getNameNotSuffix(String fileName) + { + if (fileName == null) + { + return null; + } + String baseName = FilenameUtils.getBaseName(fileName); + return baseName; + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/ImageUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/ImageUtils.java new file mode 100644 index 00000000..56863ec1 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/ImageUtils.java @@ -0,0 +1,123 @@ +package com.ruoyi.common.utils.file; + +import cn.hutool.core.codec.Base64Encoder; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.StringUtils; +import org.apache.poi.util.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.*; +import java.net.URL; +import java.net.URLConnection; +import java.util.Arrays; + +/** + * 图片处理工具类 + * + * @author ruoyi + */ +public class ImageUtils +{ + private static final Logger log = LoggerFactory.getLogger(ImageUtils.class); + + public static byte[] getImage(String imagePath) + { + InputStream is = getFile(imagePath); + try + { + return IOUtils.toByteArray(is); + } + catch (Exception e) + { + log.error("图片加载异常 {}", e); + return null; + } + finally + { + IOUtils.closeQuietly(is); + } + } + + public static InputStream getFile(String imagePath) + { + try + { + byte[] result = readFile(imagePath); + result = Arrays.copyOf(result, result.length); + return new ByteArrayInputStream(result); + } + catch (Exception e) + { + log.error("获取图片异常 {}", e); + } + return null; + } + + /** + * 读取文件为字节数据 + * + * @param url 地址 + * @return 字节数据 + */ + public static byte[] readFile(String url) + { + InputStream in = null; + try + { + if (url.startsWith("http")) + { + // 网络地址 + URL urlObj = new URL(url); + URLConnection urlConnection = urlObj.openConnection(); + urlConnection.setConnectTimeout(30 * 1000); + urlConnection.setReadTimeout(60 * 1000); + urlConnection.setDoInput(true); + in = urlConnection.getInputStream(); + } + else + { + // 本机地址 + String localPath = RuoYiConfig.getProfile(); + String downloadPath = localPath + StringUtils.substringAfter(url, Constants.RESOURCE_PREFIX); + in = new FileInputStream(downloadPath); + } + return IOUtils.toByteArray(in); + } + catch (Exception e) + { + log.error("获取文件路径异常 {}", e); + return null; + } + finally + { + IOUtils.closeQuietly(in); + } + } + /** + * 将图片内容转换成Base64编码的字符串 + * @param imageFile 图片文件的全路径名称 + * @return 转换成Base64编码的图片内容字符串 + */ + public static String getImageBase64String(String imageFile) { + if (StringUtils.isEmpty(imageFile)) { + return ""; + } + File file = new File(imageFile); + if (!file.exists()) { + return ""; + } + InputStream is = null; + byte[] data = null; + try { + is = new FileInputStream(file); + data = new byte[is.available()]; + is.read(data); + is.close(); + } catch (IOException e) { + e.printStackTrace(); + } + return Base64Encoder.encode(data); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/IoUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/IoUtils.java new file mode 100644 index 00000000..94e1f434 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/IoUtils.java @@ -0,0 +1,28 @@ +package com.ruoyi.common.utils.file; + +import cn.hutool.core.io.IORuntimeException; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.StrUtil; + +import java.io.InputStream; + +/** + * IO 工具类,用于 {@link cn.hutool.core.io.IoUtil} 缺失的方法 + * + * hasPermi + */ +public class IoUtils { + + /** + * 从流中读取 UTF8 编码的内容 + * + * @param in 输入流 + * @param isClose 是否关闭 + * @return 内容 + * @throws IORuntimeException IO 异常 + */ + public static String readUtf8(InputStream in, boolean isClose) throws IORuntimeException { + return StrUtil.utf8Str(IoUtil.read(in, isClose)); + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java new file mode 100644 index 00000000..f968f1a1 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java @@ -0,0 +1,59 @@ +package com.ruoyi.common.utils.file; + +/** + * 媒体类型工具类 + * + * @author ruoyi + */ +public class MimeTypeUtils +{ + public static final String IMAGE_PNG = "image/png"; + + public static final String IMAGE_JPG = "image/jpg"; + + public static final String IMAGE_JPEG = "image/jpeg"; + + public static final String IMAGE_BMP = "image/bmp"; + + public static final String IMAGE_GIF = "image/gif"; + + public static final String[] IMAGE_EXTENSION = { "bmp", "gif", "jpg", "jpeg", "png" }; + + public static final String[] FLASH_EXTENSION = { "swf", "flv" }; + + public static final String[] MEDIA_EXTENSION = { "swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg", + "asf", "rm", "rmvb" }; + + public static final String[] VIDEO_EXTENSION = { "mp4", "avi", "rmvb" }; + + public static final String[] DEFAULT_ALLOWED_EXTENSION = { + // 图片 + "bmp", "gif", "jpg", "jpeg", "png", + // word excel powerpoint + "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt", + // 压缩文件 + "rar", "zip", "gz", "bz2", + // 视频格式 + "mp4", "avi", "rmvb", + // pdf + "pdf" }; + + public static String getExtension(String prefix) + { + switch (prefix) + { + case IMAGE_PNG: + return "png"; + case IMAGE_JPG: + return "jpg"; + case IMAGE_JPEG: + return "jpeg"; + case IMAGE_BMP: + return "bmp"; + case IMAGE_GIF: + return "gif"; + default: + return ""; + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/EscapeUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/EscapeUtil.java new file mode 100644 index 00000000..f52e83e5 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/EscapeUtil.java @@ -0,0 +1,167 @@ +package com.ruoyi.common.utils.html; + +import com.ruoyi.common.utils.StringUtils; + +/** + * 转义和反转义工具类 + * + * @author ruoyi + */ +public class EscapeUtil +{ + public static final String RE_HTML_MARK = "(<[^<]*?>)|(<[\\s]*?/[^<]*?>)|(<[^<]*?/[\\s]*?>)"; + + private static final char[][] TEXT = new char[64][]; + + static + { + for (int i = 0; i < 64; i++) + { + TEXT[i] = new char[] { (char) i }; + } + + // special HTML characters + TEXT['\''] = "'".toCharArray(); // 单引号 + TEXT['"'] = """.toCharArray(); // 双引号 + TEXT['&'] = "&".toCharArray(); // &符 + TEXT['<'] = "<".toCharArray(); // 小于号 + TEXT['>'] = ">".toCharArray(); // 大于号 + } + + /** + * 转义文本中的HTML字符为安全的字符 + * + * @param text 被转义的文本 + * @return 转义后的文本 + */ + public static String escape(String text) + { + return encode(text); + } + + /** + * 还原被转义的HTML特殊字符 + * + * @param content 包含转义符的HTML内容 + * @return 转换后的字符串 + */ + public static String unescape(String content) + { + return decode(content); + } + + /** + * 清除所有HTML标签,但是不删除标签内的内容 + * + * @param content 文本 + * @return 清除标签后的文本 + */ + public static String clean(String content) + { + return new HTMLFilter().filter(content); + } + + /** + * Escape编码 + * + * @param text 被编码的文本 + * @return 编码后的字符 + */ + private static String encode(String text) + { + if (StringUtils.isEmpty(text)) + { + return StringUtils.EMPTY; + } + + final StringBuilder tmp = new StringBuilder(text.length() * 6); + char c; + for (int i = 0; i < text.length(); i++) + { + c = text.charAt(i); + if (c < 256) + { + tmp.append("%"); + if (c < 16) + { + tmp.append("0"); + } + tmp.append(Integer.toString(c, 16)); + } + else + { + tmp.append("%u"); + if (c <= 0xfff) + { + // issue#I49JU8@Gitee + tmp.append("0"); + } + tmp.append(Integer.toString(c, 16)); + } + } + return tmp.toString(); + } + + /** + * Escape解码 + * + * @param content 被转义的内容 + * @return 解码后的字符串 + */ + public static String decode(String content) + { + if (StringUtils.isEmpty(content)) + { + return content; + } + + StringBuilder tmp = new StringBuilder(content.length()); + int lastPos = 0, pos = 0; + char ch; + while (lastPos < content.length()) + { + pos = content.indexOf("%", lastPos); + if (pos == lastPos) + { + if (content.charAt(pos + 1) == 'u') + { + ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16); + tmp.append(ch); + lastPos = pos + 6; + } + else + { + ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16); + tmp.append(ch); + lastPos = pos + 3; + } + } + else + { + if (pos == -1) + { + tmp.append(content.substring(lastPos)); + lastPos = content.length(); + } + else + { + tmp.append(content.substring(lastPos, pos)); + lastPos = pos; + } + } + } + return tmp.toString(); + } + + public static void main(String[] args) + { + String html = ""; + String escape = EscapeUtil.escape(html); + // String html = "ipt>alert(\"XSS\")ipt>"; + // String html = "<123"; + // String html = "123>"; + System.out.println("clean: " + EscapeUtil.clean(html)); + System.out.println("escape: " + escape); + System.out.println("unescape: " + EscapeUtil.unescape(escape)); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java new file mode 100644 index 00000000..3f64f2ef --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java @@ -0,0 +1,570 @@ +package com.ruoyi.common.utils.html; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * HTML过滤器,用于去除XSS漏洞隐患。 + * + * @author ruoyi + */ +public final class HTMLFilter +{ + /** + * regex flag union representing /si modifiers in php + **/ + private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL; + private static final Pattern P_COMMENTS = Pattern.compile("", Pattern.DOTALL); + private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI); + private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL); + private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI); + private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI); + private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI); + private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI); + private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI); + private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?"); + private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?"); + private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?"); + private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))"); + private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL); + private static final Pattern P_END_ARROW = Pattern.compile("^>"); + private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_AMP = Pattern.compile("&"); + private static final Pattern P_QUOTE = Pattern.compile("\""); + private static final Pattern P_LEFT_ARROW = Pattern.compile("<"); + private static final Pattern P_RIGHT_ARROW = Pattern.compile(">"); + private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>"); + + // @xxx could grow large... maybe use sesat's ReferenceMap + private static final ConcurrentMap P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>(); + private static final ConcurrentMap P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>(); + + /** + * set of allowed html elements, along with allowed attributes for each element + **/ + private final Map> vAllowed; + /** + * counts of open tags for each (allowable) html element + **/ + private final Map vTagCounts = new HashMap<>(); + + /** + * html elements which must always be self-closing (e.g. "") + **/ + private final String[] vSelfClosingTags; + /** + * html elements which must always have separate opening and closing tags (e.g. "") + **/ + private final String[] vNeedClosingTags; + /** + * set of disallowed html elements + **/ + private final String[] vDisallowed; + /** + * attributes which should be checked for valid protocols + **/ + private final String[] vProtocolAtts; + /** + * allowed protocols + **/ + private final String[] vAllowedProtocols; + /** + * tags which should be removed if they contain no content (e.g. "" or "") + **/ + private final String[] vRemoveBlanks; + /** + * entities allowed within html markup + **/ + private final String[] vAllowedEntities; + /** + * flag determining whether comments are allowed in input String. + */ + private final boolean stripComment; + private final boolean encodeQuotes; + /** + * flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. "" + * becomes " text "). If set to false, unbalanced angle brackets will be html escaped. + */ + private final boolean alwaysMakeTags; + + /** + * Default constructor. + */ + public HTMLFilter() + { + vAllowed = new HashMap<>(); + + final ArrayList a_atts = new ArrayList<>(); + a_atts.add("href"); + a_atts.add("target"); + vAllowed.put("a", a_atts); + + final ArrayList img_atts = new ArrayList<>(); + img_atts.add("src"); + img_atts.add("width"); + img_atts.add("height"); + img_atts.add("alt"); + vAllowed.put("img", img_atts); + + final ArrayList no_atts = new ArrayList<>(); + vAllowed.put("b", no_atts); + vAllowed.put("strong", no_atts); + vAllowed.put("i", no_atts); + vAllowed.put("em", no_atts); + + vSelfClosingTags = new String[] { "img" }; + vNeedClosingTags = new String[] { "a", "b", "strong", "i", "em" }; + vDisallowed = new String[] {}; + vAllowedProtocols = new String[] { "http", "mailto", "https" }; // no ftp. + vProtocolAtts = new String[] { "src", "href" }; + vRemoveBlanks = new String[] { "a", "b", "strong", "i", "em" }; + vAllowedEntities = new String[] { "amp", "gt", "lt", "quot" }; + stripComment = true; + encodeQuotes = true; + alwaysMakeTags = false; + } + + /** + * Map-parameter configurable constructor. + * + * @param conf map containing configuration. keys match field names. + */ + @SuppressWarnings("unchecked") + public HTMLFilter(final Map conf) + { + + assert conf.containsKey("vAllowed") : "configuration requires vAllowed"; + assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags"; + assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags"; + assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed"; + assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols"; + assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts"; + assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks"; + assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities"; + + vAllowed = Collections.unmodifiableMap((HashMap>) conf.get("vAllowed")); + vSelfClosingTags = (String[]) conf.get("vSelfClosingTags"); + vNeedClosingTags = (String[]) conf.get("vNeedClosingTags"); + vDisallowed = (String[]) conf.get("vDisallowed"); + vAllowedProtocols = (String[]) conf.get("vAllowedProtocols"); + vProtocolAtts = (String[]) conf.get("vProtocolAtts"); + vRemoveBlanks = (String[]) conf.get("vRemoveBlanks"); + vAllowedEntities = (String[]) conf.get("vAllowedEntities"); + stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true; + encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true; + alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true; + } + + private void reset() + { + vTagCounts.clear(); + } + + // --------------------------------------------------------------- + // my versions of some PHP library functions + public static String chr(final int decimal) + { + return String.valueOf((char) decimal); + } + + public static String htmlSpecialChars(final String s) + { + String result = s; + result = regexReplace(P_AMP, "&", result); + result = regexReplace(P_QUOTE, """, result); + result = regexReplace(P_LEFT_ARROW, "<", result); + result = regexReplace(P_RIGHT_ARROW, ">", result); + return result; + } + + // --------------------------------------------------------------- + + /** + * given a user submitted input String, filter out any invalid or restricted html. + * + * @param input text (i.e. submitted by a user) than may contain html + * @return "clean" version of input, with only valid, whitelisted html elements allowed + */ + public String filter(final String input) + { + reset(); + String s = input; + + s = escapeComments(s); + + s = balanceHTML(s); + + s = checkTags(s); + + s = processRemoveBlanks(s); + + // s = validateEntities(s); + + return s; + } + + public boolean isAlwaysMakeTags() + { + return alwaysMakeTags; + } + + public boolean isStripComments() + { + return stripComment; + } + + private String escapeComments(final String s) + { + final Matcher m = P_COMMENTS.matcher(s); + final StringBuffer buf = new StringBuffer(); + if (m.find()) + { + final String match = m.group(1); // (.*?) + m.appendReplacement(buf, Matcher.quoteReplacement("")); + } + m.appendTail(buf); + + return buf.toString(); + } + + private String balanceHTML(String s) + { + if (alwaysMakeTags) + { + // + // try and form html + // + s = regexReplace(P_END_ARROW, "", s); + // 不追加结束标签 + s = regexReplace(P_BODY_TO_END, "<$1>", s); + s = regexReplace(P_XML_CONTENT, "$1<$2", s); + + } + else + { + // + // escape stray brackets + // + s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s); + s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s); + + // + // the last regexp causes '<>' entities to appear + // (we need to do a lookahead assertion so that the last bracket can + // be used in the next pass of the regexp) + // + s = regexReplace(P_BOTH_ARROWS, "", s); + } + + return s; + } + + private String checkTags(String s) + { + Matcher m = P_TAGS.matcher(s); + + final StringBuffer buf = new StringBuffer(); + while (m.find()) + { + String replaceStr = m.group(1); + replaceStr = processTag(replaceStr); + m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr)); + } + m.appendTail(buf); + + // these get tallied in processTag + // (remember to reset before subsequent calls to filter method) + final StringBuilder sBuilder = new StringBuilder(buf.toString()); + for (String key : vTagCounts.keySet()) + { + for (int ii = 0; ii < vTagCounts.get(key); ii++) + { + sBuilder.append(""); + } + } + s = sBuilder.toString(); + + return s; + } + + private String processRemoveBlanks(final String s) + { + String result = s; + for (String tag : vRemoveBlanks) + { + if (!P_REMOVE_PAIR_BLANKS.containsKey(tag)) + { + P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?>")); + } + result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result); + if (!P_REMOVE_SELF_BLANKS.containsKey(tag)) + { + P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>")); + } + result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result); + } + + return result; + } + + private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s) + { + Matcher m = regex_pattern.matcher(s); + return m.replaceAll(replacement); + } + + private String processTag(final String s) + { + // ending tags + Matcher m = P_END_TAG.matcher(s); + if (m.find()) + { + final String name = m.group(1).toLowerCase(); + if (allowed(name)) + { + if (!inArray(name, vSelfClosingTags)) + { + if (vTagCounts.containsKey(name)) + { + vTagCounts.put(name, vTagCounts.get(name) - 1); + return ""; + } + } + } + } + + // starting tags + m = P_START_TAG.matcher(s); + if (m.find()) + { + final String name = m.group(1).toLowerCase(); + final String body = m.group(2); + String ending = m.group(3); + + // debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" ); + if (allowed(name)) + { + final StringBuilder params = new StringBuilder(); + + final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body); + final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body); + final List paramNames = new ArrayList<>(); + final List paramValues = new ArrayList<>(); + while (m2.find()) + { + paramNames.add(m2.group(1)); // ([a-z0-9]+) + paramValues.add(m2.group(3)); // (.*?) + } + while (m3.find()) + { + paramNames.add(m3.group(1)); // ([a-z0-9]+) + paramValues.add(m3.group(3)); // ([^\"\\s']+) + } + + String paramName, paramValue; + for (int ii = 0; ii < paramNames.size(); ii++) + { + paramName = paramNames.get(ii).toLowerCase(); + paramValue = paramValues.get(ii); + + // debug( "paramName='" + paramName + "'" ); + // debug( "paramValue='" + paramValue + "'" ); + // debug( "allowed? " + vAllowed.get( name ).contains( paramName ) ); + + if (allowedAttribute(name, paramName)) + { + if (inArray(paramName, vProtocolAtts)) + { + paramValue = processParamProtocol(paramValue); + } + params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\\\""); + } + } + + if (inArray(name, vSelfClosingTags)) + { + ending = " /"; + } + + if (inArray(name, vNeedClosingTags)) + { + ending = ""; + } + + if (ending == null || ending.length() < 1) + { + if (vTagCounts.containsKey(name)) + { + vTagCounts.put(name, vTagCounts.get(name) + 1); + } + else + { + vTagCounts.put(name, 1); + } + } + else + { + ending = " /"; + } + return "<" + name + params + ending + ">"; + } + else + { + return ""; + } + } + + // comments + m = P_COMMENT.matcher(s); + if (!stripComment && m.find()) + { + return "<" + m.group() + ">"; + } + + return ""; + } + + private String processParamProtocol(String s) + { + s = decodeEntities(s); + final Matcher m = P_PROTOCOL.matcher(s); + if (m.find()) + { + final String protocol = m.group(1); + if (!inArray(protocol, vAllowedProtocols)) + { + // bad protocol, turn into local anchor link instead + s = "#" + s.substring(protocol.length() + 1); + if (s.startsWith("#//")) + { + s = "#" + s.substring(3); + } + } + } + + return s; + } + + private String decodeEntities(String s) + { + StringBuffer buf = new StringBuffer(); + + Matcher m = P_ENTITY.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.decode(match).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + buf = new StringBuffer(); + m = P_ENTITY_UNICODE.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.valueOf(match, 16).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + buf = new StringBuffer(); + m = P_ENCODE.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.valueOf(match, 16).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + s = validateEntities(s); + return s; + } + + private String validateEntities(final String s) + { + StringBuffer buf = new StringBuffer(); + + // validate entities throughout the string + Matcher m = P_VALID_ENTITIES.matcher(s); + while (m.find()) + { + final String one = m.group(1); // ([^&;]*) + final String two = m.group(2); // (?=(;|&|$)) + m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two))); + } + m.appendTail(buf); + + return encodeQuotes(buf.toString()); + } + + private String encodeQuotes(final String s) + { + if (encodeQuotes) + { + StringBuffer buf = new StringBuffer(); + Matcher m = P_VALID_QUOTES.matcher(s); + while (m.find()) + { + final String one = m.group(1); // (>|^) + final String two = m.group(2); // ([^<]+?) + final String three = m.group(3); // (<|$) + // 不替换双引号为",防止json格式无效 regexReplace(P_QUOTE, """, two) + m.appendReplacement(buf, Matcher.quoteReplacement(one + two + three)); + } + m.appendTail(buf); + return buf.toString(); + } + else + { + return s; + } + } + + private String checkEntity(final String preamble, final String term) + { + + return ";".equals(term) && isValidEntity(preamble) ? '&' + preamble : "&" + preamble; + } + + private boolean isValidEntity(final String entity) + { + return inArray(entity, vAllowedEntities); + } + + private static boolean inArray(final String s, final String[] array) + { + for (String item : array) + { + if (item != null && item.equals(s)) + { + return true; + } + } + return false; + } + + private boolean allowed(final String name) + { + return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed); + } + + private boolean allowedAttribute(final String name, final String paramName) + { + return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName)); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java new file mode 100644 index 00000000..589d1231 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java @@ -0,0 +1,55 @@ +package com.ruoyi.common.utils.http; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import javax.servlet.ServletRequest; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 通用http工具封装 + * + * @author ruoyi + */ +public class HttpHelper +{ + private static final Logger LOGGER = LoggerFactory.getLogger(HttpHelper.class); + + public static String getBodyString(ServletRequest request) + { + StringBuilder sb = new StringBuilder(); + BufferedReader reader = null; + try (InputStream inputStream = request.getInputStream()) + { + reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + String line = ""; + while ((line = reader.readLine()) != null) + { + sb.append(line); + } + } + catch (IOException e) + { + LOGGER.warn("getBodyString出现问题!"); + } + finally + { + if (reader != null) + { + try + { + reader.close(); + } + catch (IOException e) + { + LOGGER.error(ExceptionUtils.getMessage(e)); + } + } + } + return sb.toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java new file mode 100644 index 00000000..f85c82c5 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java @@ -0,0 +1,274 @@ +package com.ruoyi.common.utils.http; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.ConnectException; +import java.net.SocketTimeoutException; +import java.net.URL; +import java.net.URLConnection; +import java.nio.charset.StandardCharsets; +import java.security.cert.X509Certificate; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.StringUtils; + +/** + * 通用http发送方法 + * + * @author ruoyi + */ +public class HttpUtils +{ + private static final Logger log = LoggerFactory.getLogger(HttpUtils.class); + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url) + { + return sendGet(url, StringUtils.EMPTY); + } + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url, String param) + { + return sendGet(url, param, Constants.UTF8); + } + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @param contentType 编码类型 + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url, String param, String contentType) + { + StringBuilder result = new StringBuilder(); + BufferedReader in = null; + try + { + String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url; + log.info("sendGet - {}", urlNameString); + URL realUrl = new URL(urlNameString); + URLConnection connection = realUrl.openConnection(); + connection.setRequestProperty("accept", "*/*"); + connection.setRequestProperty("connection", "Keep-Alive"); + connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + connection.connect(); + in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType)); + String line; + while ((line = in.readLine()) != null) + { + result.append(line); + } + log.info("recv - {}", result); + } + catch (ConnectException e) + { + log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e); + } + catch (SocketTimeoutException e) + { + log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e); + } + catch (IOException e) + { + log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e); + } + catch (Exception e) + { + log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e); + } + finally + { + try + { + if (in != null) + { + in.close(); + } + } + catch (Exception ex) + { + log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); + } + } + return result.toString(); + } + + /** + * 向指定 URL 发送POST方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @return 所代表远程资源的响应结果 + */ + public static String sendPost(String url, String param) + { + PrintWriter out = null; + BufferedReader in = null; + StringBuilder result = new StringBuilder(); + try + { + log.info("sendPost - {}", url); + URL realUrl = new URL(url); + URLConnection conn = realUrl.openConnection(); + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + conn.setRequestProperty("Accept-Charset", "utf-8"); + conn.setRequestProperty("contentType", "utf-8"); + conn.setDoOutput(true); + conn.setDoInput(true); + out = new PrintWriter(conn.getOutputStream()); + out.print(param); + out.flush(); + in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8)); + String line; + while ((line = in.readLine()) != null) + { + result.append(line); + } + log.info("recv - {}", result); + } + catch (ConnectException e) + { + log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e); + } + catch (SocketTimeoutException e) + { + log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e); + } + catch (IOException e) + { + log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e); + } + catch (Exception e) + { + log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e); + } + finally + { + try + { + if (out != null) + { + out.close(); + } + if (in != null) + { + in.close(); + } + } + catch (IOException ex) + { + log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); + } + } + return result.toString(); + } + + public static String sendSSLPost(String url, String param) + { + StringBuilder result = new StringBuilder(); + String urlNameString = url + "?" + param; + try + { + log.info("sendSSLPost - {}", urlNameString); + SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom()); + URL console = new URL(urlNameString); + HttpsURLConnection conn = (HttpsURLConnection) console.openConnection(); + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + conn.setRequestProperty("Accept-Charset", "utf-8"); + conn.setRequestProperty("contentType", "utf-8"); + conn.setDoOutput(true); + conn.setDoInput(true); + + conn.setSSLSocketFactory(sc.getSocketFactory()); + conn.setHostnameVerifier(new TrustAnyHostnameVerifier()); + conn.connect(); + InputStream is = conn.getInputStream(); + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + String ret = ""; + while ((ret = br.readLine()) != null) + { + if (ret != null && !"".equals(ret.trim())) + { + result.append(new String(ret.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8)); + } + } + log.info("recv - {}", result); + conn.disconnect(); + br.close(); + } + catch (ConnectException e) + { + log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e); + } + catch (SocketTimeoutException e) + { + log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e); + } + catch (IOException e) + { + log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e); + } + catch (Exception e) + { + log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e); + } + return result.toString(); + } + + private static class TrustAnyTrustManager implements X509TrustManager + { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) + { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) + { + } + + @Override + public X509Certificate[] getAcceptedIssuers() + { + return new X509Certificate[] {}; + } + } + + private static class TrustAnyHostnameVerifier implements HostnameVerifier + { + @Override + public boolean verify(String hostname, SSLSession session) + { + return true; + } + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java new file mode 100644 index 00000000..edfe419a --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java @@ -0,0 +1,56 @@ +package com.ruoyi.common.utils.ip; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.http.HttpUtils; + +/** + * 获取地址类 + * + * @author ruoyi + */ +public class AddressUtils +{ + private static final Logger log = LoggerFactory.getLogger(AddressUtils.class); + + // IP地址查询 + public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp"; + + // 未知地址 + public static final String UNKNOWN = "XX XX"; + + public static String getRealAddressByIP(String ip) + { + // 内网不查询 + if (IpUtils.internalIp(ip)) + { + return "内网IP"; + } + if (RuoYiConfig.isAddressEnabled()) + { + try + { + String rspStr = HttpUtils.sendGet(IP_URL, "ip=" + ip + "&json=true", Constants.GBK); + if (StringUtils.isEmpty(rspStr)) + { + log.error("获取地理位置异常 {}", ip); + return UNKNOWN; + } + JSONObject obj = JSON.parseObject(rspStr); + String region = obj.getString("pro"); + String city = obj.getString("city"); + return String.format("%s %s", region, city); + } + catch (Exception e) + { + log.error("获取地理位置异常 {}", ip); + } + } + return UNKNOWN; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java new file mode 100644 index 00000000..c18c56a4 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java @@ -0,0 +1,264 @@ +package com.ruoyi.common.utils.ip; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import javax.servlet.http.HttpServletRequest; +import com.ruoyi.common.utils.StringUtils; + +/** + * 获取IP方法 + * + * @author ruoyi + */ +public class IpUtils +{ + /** + * 获取客户端IP + * + * @param request 请求对象 + * @return IP地址 + */ + public static String getIpAddr(HttpServletRequest request) + { + if (request == null) + { + return "unknown"; + } + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("X-Forwarded-For"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("X-Real-IP"); + } + + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getRemoteAddr(); + } + + return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip); + } + + /** + * 检查是否为内部IP地址 + * + * @param ip IP地址 + * @return 结果 + */ + public static boolean internalIp(String ip) + { + byte[] addr = textToNumericFormatV4(ip); + return internalIp(addr) || "127.0.0.1".equals(ip); + } + + /** + * 检查是否为内部IP地址 + * + * @param addr byte地址 + * @return 结果 + */ + private static boolean internalIp(byte[] addr) + { + if (StringUtils.isNull(addr) || addr.length < 2) + { + return true; + } + final byte b0 = addr[0]; + final byte b1 = addr[1]; + // 10.x.x.x/8 + final byte SECTION_1 = 0x0A; + // 172.16.x.x/12 + final byte SECTION_2 = (byte) 0xAC; + final byte SECTION_3 = (byte) 0x10; + final byte SECTION_4 = (byte) 0x1F; + // 192.168.x.x/16 + final byte SECTION_5 = (byte) 0xC0; + final byte SECTION_6 = (byte) 0xA8; + switch (b0) + { + case SECTION_1: + return true; + case SECTION_2: + if (b1 >= SECTION_3 && b1 <= SECTION_4) + { + return true; + } + case SECTION_5: + switch (b1) + { + case SECTION_6: + return true; + } + default: + return false; + } + } + + /** + * 将IPv4地址转换成字节 + * + * @param text IPv4地址 + * @return byte 字节 + */ + public static byte[] textToNumericFormatV4(String text) + { + if (text.length() == 0) + { + return null; + } + + byte[] bytes = new byte[4]; + String[] elements = text.split("\\.", -1); + try + { + long l; + int i; + switch (elements.length) + { + case 1: + l = Long.parseLong(elements[0]); + if ((l < 0L) || (l > 4294967295L)) + { + return null; + } + bytes[0] = (byte) (int) (l >> 24 & 0xFF); + bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 2: + l = Integer.parseInt(elements[0]); + if ((l < 0L) || (l > 255L)) + { + return null; + } + bytes[0] = (byte) (int) (l & 0xFF); + l = Integer.parseInt(elements[1]); + if ((l < 0L) || (l > 16777215L)) + { + return null; + } + bytes[1] = (byte) (int) (l >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 3: + for (i = 0; i < 2; ++i) + { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) + { + return null; + } + bytes[i] = (byte) (int) (l & 0xFF); + } + l = Integer.parseInt(elements[2]); + if ((l < 0L) || (l > 65535L)) + { + return null; + } + bytes[2] = (byte) (int) (l >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 4: + for (i = 0; i < 4; ++i) + { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) + { + return null; + } + bytes[i] = (byte) (int) (l & 0xFF); + } + break; + default: + return null; + } + } + catch (NumberFormatException e) + { + return null; + } + return bytes; + } + + /** + * 获取IP地址 + * + * @return 本地IP地址 + */ + public static String getHostIp() + { + try + { + return InetAddress.getLocalHost().getHostAddress(); + } + catch (UnknownHostException e) + { + } + return "127.0.0.1"; + } + + /** + * 获取主机名 + * + * @return 本地主机名 + */ + public static String getHostName() + { + try + { + return InetAddress.getLocalHost().getHostName(); + } + catch (UnknownHostException e) + { + } + return "未知"; + } + + /** + * 从多级反向代理中获得第一个非unknown IP地址 + * + * @param ip 获得的IP地址 + * @return 第一个非unknown IP地址 + */ + public static String getMultistageReverseProxyIp(String ip) + { + // 多级反向代理检测 + if (ip != null && ip.indexOf(",") > 0) + { + final String[] ips = ip.trim().split(","); + for (String subIp : ips) + { + if (false == isUnknown(subIp)) + { + ip = subIp; + break; + } + } + } + return ip; + } + + /** + * 检测给定字符串是否为未知,多用于检测HTTP请求相关 + * + * @param checkString 被检测的字符串 + * @return 是否未知 + */ + public static boolean isUnknown(String checkString) + { + return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString); + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelHandlerAdapter.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelHandlerAdapter.java new file mode 100644 index 00000000..5ea74c11 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelHandlerAdapter.java @@ -0,0 +1,19 @@ +package com.ruoyi.common.utils.poi; + +/** + * Excel数据格式处理适配器 + * + * @author ruoyi + */ +public interface ExcelHandlerAdapter +{ + /** + * 格式化 + * + * @param value 单元格数据值 + * @param args excel注解args参数组 + * + * @return 处理后的值 + */ + Object format(Object value, String[] args); +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java new file mode 100644 index 00000000..8e8c3fab --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java @@ -0,0 +1,1358 @@ +package com.ruoyi.common.utils.poi; + +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.annotation.Excel.Type; +import com.ruoyi.common.annotation.Excels; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.exception.UtilException; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.DictUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.file.FileTypeUtils; +import com.ruoyi.common.utils.file.FileUtils; +import com.ruoyi.common.utils.file.ImageUtils; +import com.ruoyi.common.utils.reflect.ReflectUtils; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.RegExUtils; +import org.apache.commons.lang3.reflect.FieldUtils; +import org.apache.poi.hssf.usermodel.*; +import org.apache.poi.ooxml.POIXMLDocumentPart; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.CellRangeAddressList; +import org.apache.poi.util.IOUtils; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.apache.poi.xssf.usermodel.*; +import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.math.BigDecimal; +import java.text.DecimalFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; + +/** + * Excel相关处理 + * + * @author ruoyi + */ +public class ExcelUtil { + private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class); + + public static final String FORMULA_REGEX_STR = "=|-|\\+|@"; + + public static final String[] FORMULA_STR = {"=", "-", "+", "@"}; + + /** + * Excel sheet最大行数,默认65536 + */ + public static final int sheetSize = 65536; + + /** + * 工作表名称 + */ + private String sheetName; + + /** + * 导出类型(EXPORT:导出数据;IMPORT:导入模板) + */ + private Type type; + + /** + * 工作薄对象 + */ + private Workbook wb; + + /** + * 工作表对象 + */ + private Sheet sheet; + + /** + * 样式列表 + */ + private Map styles; + + /** + * 导入导出数据列表 + */ + private List list; + + /** + * 注解列表 + */ + private List fields; + + /** + * 当前行号 + */ + private int rownum; + + /** + * 标题 + */ + private String title; + + /** + * 最大高度 + */ + private short maxHeight; + + /** + * 合并后最后行数 + */ + private int subMergedLastRowNum = 0; + + /** + * 合并后开始行数 + */ + private int subMergedFirstRowNum = 1; + + /** + * 对象的子列表方法 + */ + private Method subMethod; + + /** + * 对象的子列表属性 + */ + private List subFields; + + /** + * 统计列表 + */ + private Map statistics = new HashMap(); + + /** + * 数字格式 + */ + private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00"); + + /** + * 实体对象 + */ + public Class clazz; + + /** + * 需要排除列属性 + */ + public String[] excludeFields; + + public ExcelUtil(Class clazz) { + this.clazz = clazz; + } + + /** + * 隐藏Excel中列属性 + * + * @param fields 列属性名 示例[单个"name"/多个"id","name"] + * @throws Exception + */ + public void hideColumn(String... fields) { + this.excludeFields = fields; + } + + public void init(List list, String sheetName, String title, Type type) { + if (list == null) { + list = new ArrayList(); + } + this.list = list; + this.sheetName = sheetName; + this.type = type; + this.title = title; + createExcelField(); + createWorkbook(); + createTitle(); + createSubHead(); + } + + /** + * 创建excel第一行标题 + */ + public void createTitle() { + if (StringUtils.isNotEmpty(title)) { + subMergedFirstRowNum++; + subMergedLastRowNum++; + int titleLastCol = this.fields.size() - 1; + if (isSubList()) { + titleLastCol = titleLastCol + subFields.size() - 1; + } + Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0); + titleRow.setHeightInPoints(30); + Cell titleCell = titleRow.createCell(0); + titleCell.setCellStyle(styles.get("title")); + titleCell.setCellValue(title); + sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(), titleLastCol)); + } + } + + /** + * 创建对象的子列表名称 + */ + public void createSubHead() { + if (isSubList()) { + subMergedFirstRowNum++; + subMergedLastRowNum++; + Row subRow = sheet.createRow(rownum); + int excelNum = 0; + for (Object[] objects : fields) { + Excel attr = (Excel) objects[1]; + Cell headCell1 = subRow.createCell(excelNum); + headCell1.setCellValue(attr.name()); + headCell1.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor()))); + excelNum++; + } + int headFirstRow = excelNum - 1; + int headLastRow = headFirstRow + subFields.size() - 1; + if (headLastRow > headFirstRow) { + sheet.addMergedRegion(new CellRangeAddress(rownum, rownum, headFirstRow, headLastRow)); + } + rownum++; + } + } + + /** + * 对excel表单默认第一个索引名转换成list + * + * @param is 输入流 + * @return 转换后集合 + */ + public List importExcel(InputStream is) throws Exception { + return importExcel(is, 0); + } + + /** + * 对excel表单默认第一个索引名转换成list + * + * @param is 输入流 + * @param titleNum 标题占用行数 + * @return 转换后集合 + */ + public List importExcel(InputStream is, int titleNum) throws Exception { + return importExcel(StringUtils.EMPTY, is, titleNum); + } + + /** + * 对excel表单指定表格索引名转换成list + * + * @param sheetName 表格索引名 + * @param titleNum 标题占用行数 + * @param is 输入流 + * @return 转换后集合 + */ + public List importExcel(String sheetName, InputStream is, int titleNum) throws Exception { + this.type = Type.IMPORT; + this.wb = WorkbookFactory.create(is); + List list = new ArrayList(); + // 如果指定sheet名,则取指定sheet中的内容 否则默认指向第1个sheet + Sheet sheet = StringUtils.isNotEmpty(sheetName) ? wb.getSheet(sheetName) : wb.getSheetAt(0); + if (sheet == null) { + throw new IOException("文件sheet不存在"); + } + boolean isXSSFWorkbook = !(wb instanceof HSSFWorkbook); + Map pictures; + if (isXSSFWorkbook) { + pictures = getSheetPictures07((XSSFSheet) sheet, (XSSFWorkbook) wb); + } else { + pictures = getSheetPictures03((HSSFSheet) sheet, (HSSFWorkbook) wb); + } + // 获取最后一个非空行的行下标,比如总行数为n,则返回的为n-1 + int rows = sheet.getLastRowNum(); + + if (rows > 0) { + // 定义一个map用于存放excel列的序号和field. + Map cellMap = new HashMap(); + // 获取表头 + Row heard = sheet.getRow(titleNum); + for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++) { + Cell cell = heard.getCell(i); + if (StringUtils.isNotNull(cell)) { + String value = this.getCellValue(heard, i).toString(); + cellMap.put(value, i); + } else { + cellMap.put(null, i); + } + } + // 有数据时才处理 得到类的所有field. + List fields = this.getFields(); + Map fieldsMap = new HashMap(); + for (Object[] objects : fields) { + Excel attr = (Excel) objects[1]; + Integer column = cellMap.get(attr.name()); + if (column != null) { + fieldsMap.put(column, objects); + } + } + for (int i = titleNum + 1; i <= rows; i++) { + // 从第2行开始取数据,默认第一行是表头. + Row row = sheet.getRow(i); + // 判断当前行是否是空行 + if (isRowEmpty(row)) { + continue; + } + T entity = null; + for (Map.Entry entry : fieldsMap.entrySet()) { + Object val = this.getCellValue(row, entry.getKey()); + + // 如果不存在实例则新建. + entity = (entity == null ? clazz.newInstance() : entity); + // 从map中得到对应列的field. + Field field = (Field) entry.getValue()[0]; + Excel attr = (Excel) entry.getValue()[1]; + // 取得类型,并根据对象类型设置值. + Class fieldType = field.getType(); + if (String.class == fieldType) { + String s = Convert.toStr(val); + if (StringUtils.endsWith(s, ".0")) { + val = StringUtils.substringBefore(s, ".0"); + } else { + String dateFormat = field.getAnnotation(Excel.class).dateFormat(); + if (StringUtils.isNotEmpty(dateFormat)) { + val = parseDateToStr(dateFormat, val); + } else { + val = Convert.toStr(val); + } + } + } else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) { + val = Convert.toInt(val); + } else if ((Long.TYPE == fieldType || Long.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) { + val = Convert.toLong(val); + } else if (Double.TYPE == fieldType || Double.class == fieldType) { + val = Convert.toDouble(val); + } else if (Float.TYPE == fieldType || Float.class == fieldType) { + val = Convert.toFloat(val); + } else if (BigDecimal.class == fieldType) { + val = Convert.toBigDecimal(val); + } else if (Date.class == fieldType) { + if (val instanceof String) { + val = DateUtils.parseDate(val); + } else if (val instanceof Double) { + val = DateUtil.getJavaDate((Double) val); + } + } else if (Boolean.TYPE == fieldType || Boolean.class == fieldType) { + val = Convert.toBool(val, false); + } + if (StringUtils.isNotNull(fieldType)) { + String propertyName = field.getName(); + if (StringUtils.isNotEmpty(attr.targetAttr())) { + propertyName = field.getName() + "." + attr.targetAttr(); + } else if (StringUtils.isNotEmpty(attr.readConverterExp())) { + val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator()); + } else if (StringUtils.isNotEmpty(attr.dictType())) { + val = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator()); + } else if (!attr.handler().equals(ExcelHandlerAdapter.class)) { + val = dataFormatHandlerAdapter(val, attr); + } else if (ColumnType.IMAGE == attr.cellType() && StringUtils.isNotEmpty(pictures)) { + PictureData image = pictures.get(row.getRowNum() + "_" + entry.getKey()); + if (image == null) { + val = ""; + } else { + byte[] data = image.getData(); + val = FileUtils.writeImportBytes(data); + } + } + ReflectUtils.invokeSetter(entity, propertyName, val); + } + } + list.add(entity); + } + } + return list; + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @return 结果 + */ + public AjaxResult exportExcel(List list, String sheetName) { + return exportExcel(list, sheetName, StringUtils.EMPTY); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param title 标题 + * @return 结果 + */ + public AjaxResult exportExcel(List list, String sheetName, String title) { + this.init(list, sheetName, title, Type.EXPORT); + return exportExcel(); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param response 返回数据 + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @return 结果 + */ + public void exportExcel(HttpServletResponse response, List list, String sheetName) { + exportExcel(response, list, sheetName, StringUtils.EMPTY); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param response 返回数据 + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param title 标题 + * @return 结果 + */ + public void exportExcel(HttpServletResponse response, List list, String sheetName, String title) { + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + this.init(list, sheetName, title, Type.EXPORT); + exportExcel(response); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * @return 结果 + */ + public AjaxResult importTemplateExcel(String sheetName) { + return importTemplateExcel(sheetName, StringUtils.EMPTY); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * @param title 标题 + * @return 结果 + */ + public AjaxResult importTemplateExcel(String sheetName, String title) { + this.init(null, sheetName, title, Type.IMPORT); + return exportExcel(); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * @return 结果 + */ + public void importTemplateExcel(HttpServletResponse response, String sheetName) { + importTemplateExcel(response, sheetName, StringUtils.EMPTY); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * @param title 标题 + * @return 结果 + */ + public void importTemplateExcel(HttpServletResponse response, String sheetName, String title) { + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + this.init(null, sheetName, title, Type.IMPORT); + exportExcel(response); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @return 结果 + */ + public void exportExcel(HttpServletResponse response) { + try { + writeSheet(); + wb.write(response.getOutputStream()); + } catch (Exception e) { + log.error("导出Excel异常{}", e.getMessage()); + } finally { + IOUtils.closeQuietly(wb); + } + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @return 结果 + */ + public AjaxResult exportExcel() { + OutputStream out = null; + try { + writeSheet(); + String filename = encodingFilename(sheetName); + out = new FileOutputStream(getAbsoluteFile(filename)); + wb.write(out); + return AjaxResult.success(filename); + } catch (Exception e) { + log.error("导出Excel异常{}", e.getMessage()); + throw new UtilException("导出Excel失败,请联系网站管理员!"); + } finally { + IOUtils.closeQuietly(wb); + IOUtils.closeQuietly(out); + } + } + + /** + * 创建写入数据到Sheet + */ + public void writeSheet() { + // 取出一共有多少个sheet. + int sheetNo = Math.max(1, (int) Math.ceil(list.size() * 1.0 / sheetSize)); + for (int index = 0; index < sheetNo; index++) { + createSheet(sheetNo, index); + + // 产生一行 + Row row = sheet.createRow(rownum); + int column = 0; + // 写入各个字段的列头名称 + for (Object[] os : fields) { + Field field = (Field) os[0]; + Excel excel = (Excel) os[1]; + if (Collection.class.isAssignableFrom(field.getType())) { + for (Field subField : subFields) { + Excel subExcel = subField.getAnnotation(Excel.class); + this.createHeadCell(subExcel, row, column++); + } + } else { + this.createHeadCell(excel, row, column++); + } + } + if (Type.EXPORT.equals(type)) { + fillExcelData(index, row); + addStatisticsRow(); + } + } + } + + /** + * 填充excel数据 + * + * @param index 序号 + * @param row 单元格行 + */ + @SuppressWarnings("unchecked") + public void fillExcelData(int index, Row row) { + int startNo = index * sheetSize; + int endNo = Math.min(startNo + sheetSize, list.size()); + int rowNo = (1 + rownum) - startNo; + for (int i = startNo; i < endNo; i++) { + rowNo = i > 1 ? rowNo + 1 : rowNo + i; + row = sheet.createRow(rowNo); + // 得到导出对象. + T vo = (T) list.get(i); + Collection subList = null; + if (isSubList()) { + if (isSubListValue(vo)) { + subList = getListCellValue(vo); + subMergedLastRowNum = subMergedLastRowNum + subList.size(); + } else { + subMergedFirstRowNum++; + subMergedLastRowNum++; + } + } + + int column = 0; + for (Object[] os : fields) { + Field field = (Field) os[0]; + Excel excel = (Excel) os[1]; + if (Collection.class.isAssignableFrom(field.getType()) && StringUtils.isNotNull(subList)) { + boolean subFirst = false; + for (Object obj : subList) { + if (subFirst) { + rowNo++; + row = sheet.createRow(rowNo); + } + List subFields = FieldUtils.getFieldsListWithAnnotation(obj.getClass(), Excel.class); + int subIndex = 0; + for (Field subField : subFields) { + if (subField.isAnnotationPresent(Excel.class)) { + subField.setAccessible(true); + Excel attr = subField.getAnnotation(Excel.class); + this.addCell(attr, row, (T) obj, subField, column + subIndex); + } + subIndex++; + } + subFirst = true; + } + this.subMergedFirstRowNum = this.subMergedFirstRowNum + subList.size(); + } else { + this.addCell(excel, row, vo, field, column++); + } + } + } + } + + /** + * 创建表格样式 + * + * @param wb 工作薄对象 + * @return 样式列表 + */ + private Map createStyles(Workbook wb) { + // 写入各条记录,每条记录对应excel表中的一行 + Map styles = new HashMap(); + CellStyle style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + Font titleFont = wb.createFont(); + titleFont.setFontName("Arial"); + titleFont.setFontHeightInPoints((short) 16); + titleFont.setBold(true); + style.setFont(titleFont); + styles.put("title", style); + + style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setBorderRight(BorderStyle.THIN); + style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderLeft(BorderStyle.THIN); + style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderTop(BorderStyle.THIN); + style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderBottom(BorderStyle.THIN); + style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + Font dataFont = wb.createFont(); + dataFont.setFontName("Arial"); + dataFont.setFontHeightInPoints((short) 10); + style.setFont(dataFont); + styles.put("data", style); + + style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + Font totalFont = wb.createFont(); + totalFont.setFontName("Arial"); + totalFont.setFontHeightInPoints((short) 10); + style.setFont(totalFont); + styles.put("total", style); + + styles.putAll(annotationHeaderStyles(wb, styles)); + + styles.putAll(annotationDataStyles(wb)); + + return styles; + } + + /** + * 根据Excel注解创建表格头样式 + * + * @param wb 工作薄对象 + * @return 自定义样式列表 + */ + private Map annotationHeaderStyles(Workbook wb, Map styles) { + Map headerStyles = new HashMap(); + for (Object[] os : fields) { + Excel excel = (Excel) os[1]; + String key = StringUtils.format("header_{}_{}", excel.headerColor(), excel.headerBackgroundColor()); + if (!headerStyles.containsKey(key)) { + CellStyle style = wb.createCellStyle(); + style.cloneStyleFrom(styles.get("data")); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setFillForegroundColor(excel.headerBackgroundColor().index); + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); + Font headerFont = wb.createFont(); + headerFont.setFontName("Arial"); + headerFont.setFontHeightInPoints((short) 10); + headerFont.setBold(true); + headerFont.setColor(excel.headerColor().index); + style.setFont(headerFont); + headerStyles.put(key, style); + } + } + return headerStyles; + } + + /** + * 根据Excel注解创建表格列样式 + * + * @param wb 工作薄对象 + * @return 自定义样式列表 + */ + private Map annotationDataStyles(Workbook wb) { + Map styles = new HashMap(); + for (Object[] os : fields) { + Excel excel = (Excel) os[1]; + String key = StringUtils.format("data_{}_{}_{}", excel.align(), excel.color(), excel.backgroundColor()); + if (!styles.containsKey(key)) { + CellStyle style = wb.createCellStyle(); + style.setAlignment(excel.align()); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setBorderRight(BorderStyle.THIN); + style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderLeft(BorderStyle.THIN); + style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderTop(BorderStyle.THIN); + style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderBottom(BorderStyle.THIN); + style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); + style.setFillForegroundColor(excel.backgroundColor().getIndex()); + Font dataFont = wb.createFont(); + dataFont.setFontName("Arial"); + dataFont.setFontHeightInPoints((short) 10); + dataFont.setColor(excel.color().index); + style.setFont(dataFont); + styles.put(key, style); + } + } + return styles; + } + + /** + * 创建单元格 + */ + public Cell createHeadCell(Excel attr, Row row, int column) { + // 创建列 + Cell cell = row.createCell(column); + // 写入列信息 + cell.setCellValue(attr.name()); + setDataValidation(attr, row, column); + cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor()))); + if (isSubList()) { + // 填充默认样式,防止合并单元格样式失效 + sheet.setDefaultColumnStyle(column, styles.get(StringUtils.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor()))); + if (attr.needMerge()) { + sheet.addMergedRegion(new CellRangeAddress(rownum - 1, rownum, column, column)); + } + } + return cell; + } + + /** + * 设置单元格信息 + * + * @param value 单元格值 + * @param attr 注解相关 + * @param cell 单元格信息 + */ + public void setCellVo(Object value, Excel attr, Cell cell) { + if (ColumnType.STRING == attr.cellType()) { + String cellValue = Convert.toStr(value); + // 对于任何以表达式触发字符 =-+@开头的单元格,直接使用tab字符作为前缀,防止CSV注入。 + if (StringUtils.startsWithAny(cellValue, FORMULA_STR)) { + cellValue = RegExUtils.replaceFirst(cellValue, FORMULA_REGEX_STR, "\t$0"); + } + cell.setCellValue(StringUtils.isNull(cellValue) ? attr.defaultValue() : cellValue + attr.suffix()); + } else if (ColumnType.NUMERIC == attr.cellType()) { + if (StringUtils.isNotNull(value)) { + cell.setCellValue(StringUtils.contains(Convert.toStr(value), ".") ? Convert.toDouble(value) : Convert.toInt(value)); + } + } else if (ColumnType.IMAGE == attr.cellType()) { + ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1), cell.getRow().getRowNum() + 1); + String imagePath = Convert.toStr(value); + if (StringUtils.isNotEmpty(imagePath)) { + byte[] data = ImageUtils.getImage(imagePath); + getDrawingPatriarch(cell.getSheet()).createPicture(anchor, + cell.getSheet().getWorkbook().addPicture(data, getImageType(data))); + } + } + } + + /** + * 获取画布 + */ + public static Drawing getDrawingPatriarch(Sheet sheet) { + if (sheet.getDrawingPatriarch() == null) { + sheet.createDrawingPatriarch(); + } + return sheet.getDrawingPatriarch(); + } + + /** + * 获取图片类型,设置图片插入类型 + */ + public int getImageType(byte[] value) { + String type = FileTypeUtils.getFileExtendName(value); + if ("JPG".equalsIgnoreCase(type)) { + return Workbook.PICTURE_TYPE_JPEG; + } else if ("PNG".equalsIgnoreCase(type)) { + return Workbook.PICTURE_TYPE_PNG; + } + return Workbook.PICTURE_TYPE_JPEG; + } + + /** + * 创建表格样式 + */ + public void setDataValidation(Excel attr, Row row, int column) { + if (attr.name().indexOf("注:") >= 0) { + sheet.setColumnWidth(column, 6000); + } else { + // 设置列宽 + sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256)); + } + if (StringUtils.isNotEmpty(attr.prompt()) || attr.combo().length > 0) { + // 提示信息或只能选择不能输入的列内容. + setPromptOrValidation(sheet, attr.combo(), attr.prompt(), 1, 100, column, column); + } + } + + /** + * 添加单元格 + */ + public Cell addCell(Excel attr, Row row, T vo, Field field, int column) { + Cell cell = null; + try { + // 设置行高 + row.setHeight(maxHeight); + // 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列. + if (attr.isExport()) { + // 创建cell + cell = row.createCell(column); + if (isSubListValue(vo) && getListCellValue(vo).size() > 1 && attr.needMerge()) { + CellRangeAddress cellAddress = new CellRangeAddress(subMergedFirstRowNum, subMergedLastRowNum, column, column); + sheet.addMergedRegion(cellAddress); + } + cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor()))); + + // 用于读取对象中的属性 + Object value = getTargetValue(vo, field, attr); + String dateFormat = attr.dateFormat(); + String readConverterExp = attr.readConverterExp(); + String separator = attr.separator(); + String dictType = attr.dictType(); + if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value)) { + cell.setCellValue(parseDateToStr(dateFormat, value)); + } else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value)) { + cell.setCellValue(convertByExp(Convert.toStr(value), readConverterExp, separator)); + } else if (StringUtils.isNotEmpty(dictType) && StringUtils.isNotNull(value)) { + cell.setCellValue(convertDictByExp(Convert.toStr(value), dictType, separator)); + } else if (value instanceof BigDecimal && -1 != attr.scale()) { + cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).doubleValue()); + } else if (!attr.handler().equals(ExcelHandlerAdapter.class)) { + cell.setCellValue(dataFormatHandlerAdapter(value, attr)); + } else { + // 设置列类型 + setCellVo(value, attr, cell); + } + addStatisticsData(column, Convert.toStr(value), attr); + } + } catch (Exception e) { + log.error("导出Excel失败{}", e); + } + return cell; + } + + /** + * 设置 POI XSSFSheet 单元格提示或选择框 + * + * @param sheet 表单 + * @param textlist 下拉框显示的内容 + * @param promptContent 提示内容 + * @param firstRow 开始行 + * @param endRow 结束行 + * @param firstCol 开始列 + * @param endCol 结束列 + */ + public void setPromptOrValidation(Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow, + int firstCol, int endCol) { + DataValidationHelper helper = sheet.getDataValidationHelper(); + DataValidationConstraint constraint = textlist.length > 0 ? helper.createExplicitListConstraint(textlist) : helper.createCustomConstraint("DD1"); + CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); + DataValidation dataValidation = helper.createValidation(constraint, regions); + if (StringUtils.isNotEmpty(promptContent)) { + // 如果设置了提示信息则鼠标放上去提示 + dataValidation.createPromptBox("", promptContent); + dataValidation.setShowPromptBox(true); + } + // 处理Excel兼容性问题 + if (dataValidation instanceof XSSFDataValidation) { + dataValidation.setSuppressDropDownArrow(true); + dataValidation.setShowErrorBox(true); + } else { + dataValidation.setSuppressDropDownArrow(false); + } + sheet.addValidationData(dataValidation); + } + + /** + * 解析导出值 0=男,1=女,2=未知 + * + * @param propertyValue 参数值 + * @param converterExp 翻译注解 + * @param separator 分隔符 + * @return 解析后值 + */ + public static String convertByExp(String propertyValue, String converterExp, String separator) { + StringBuilder propertyString = new StringBuilder(); + String[] convertSource = converterExp.split(","); + for (String item : convertSource) { + String[] itemArray = item.split("="); + if (StringUtils.containsAny(propertyValue, separator)) { + for (String value : propertyValue.split(separator)) { + if (itemArray[0].equals(value)) { + propertyString.append(itemArray[1] + separator); + break; + } + } + } else { + if (itemArray[0].equals(propertyValue)) { + return itemArray[1]; + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 反向解析值 男=0,女=1,未知=2 + * + * @param propertyValue 参数值 + * @param converterExp 翻译注解 + * @param separator 分隔符 + * @return 解析后值 + */ + public static String reverseByExp(String propertyValue, String converterExp, String separator) { + StringBuilder propertyString = new StringBuilder(); + String[] convertSource = converterExp.split(","); + for (String item : convertSource) { + String[] itemArray = item.split("="); + if (StringUtils.containsAny(propertyValue, separator)) { + for (String value : propertyValue.split(separator)) { + if (itemArray[1].equals(value)) { + propertyString.append(itemArray[0] + separator); + break; + } + } + } else { + if (itemArray[1].equals(propertyValue)) { + return itemArray[0]; + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 解析字典值 + * + * @param dictValue 字典值 + * @param dictType 字典类型 + * @param separator 分隔符 + * @return 字典标签 + */ + public static String convertDictByExp(String dictValue, String dictType, String separator) { + return DictUtils.getDictLabel(dictType, dictValue, separator); + } + + /** + * 反向解析值字典值 + * + * @param dictLabel 字典标签 + * @param dictType 字典类型 + * @param separator 分隔符 + * @return 字典值 + */ + public static String reverseDictByExp(String dictLabel, String dictType, String separator) { + return DictUtils.getDictValue(dictType, dictLabel, separator); + } + + /** + * 数据处理器 + * + * @param value 数据值 + * @param excel 数据注解 + * @return + */ + public String dataFormatHandlerAdapter(Object value, Excel excel) { + try { + Object instance = excel.handler().newInstance(); + Method formatMethod = excel.handler().getMethod("format", new Class[]{Object.class, String[].class}); + value = formatMethod.invoke(instance, value, excel.args()); + } catch (Exception e) { + log.error("不能格式化数据 " + excel.handler(), e.getMessage()); + } + return Convert.toStr(value); + } + + /** + * 合计统计信息 + */ + private void addStatisticsData(Integer index, String text, Excel entity) { + if (entity != null && entity.isStatistics()) { + Double temp = 0D; + if (!statistics.containsKey(index)) { + statistics.put(index, temp); + } + try { + temp = Double.valueOf(text); + } catch (NumberFormatException e) { + } + statistics.put(index, statistics.get(index) + temp); + } + } + + /** + * 创建统计行 + */ + public void addStatisticsRow() { + if (statistics.size() > 0) { + Row row = sheet.createRow(sheet.getLastRowNum() + 1); + Set keys = statistics.keySet(); + Cell cell = row.createCell(0); + cell.setCellStyle(styles.get("total")); + cell.setCellValue("合计"); + + for (Integer key : keys) { + cell = row.createCell(key); + cell.setCellStyle(styles.get("total")); + cell.setCellValue(DOUBLE_FORMAT.format(statistics.get(key))); + } + statistics.clear(); + } + } + + /** + * 编码文件名 + */ + public String encodingFilename(String filename) { + filename = UUID.randomUUID().toString() + "_" + filename + ".xlsx"; + return filename; + } + + /** + * 获取下载路径 + * + * @param filename 文件名称 + */ + public String getAbsoluteFile(String filename) { + String downloadPath = RuoYiConfig.getDownloadPath() + filename; + File desc = new File(downloadPath); + if (!desc.getParentFile().exists()) { + desc.getParentFile().mkdirs(); + } + return downloadPath; + } + + /** + * 获取bean中的属性值 + * + * @param vo 实体对象 + * @param field 字段 + * @param excel 注解 + * @return 最终的属性值 + * @throws Exception + */ + private Object getTargetValue(T vo, Field field, Excel excel) throws Exception { + Object o = field.get(vo); + if (StringUtils.isNotEmpty(excel.targetAttr())) { + String target = excel.targetAttr(); + if (target.contains(".")) { + String[] targets = target.split("[.]"); + for (String name : targets) { + o = getValue(o, name); + } + } else { + o = getValue(o, target); + } + } + return o; + } + + /** + * 以类的属性的get方法方法形式获取值 + * + * @param o + * @param name + * @return value + * @throws Exception + */ + private Object getValue(Object o, String name) throws Exception { + if (StringUtils.isNotNull(o) && StringUtils.isNotEmpty(name)) { + Class clazz = o.getClass(); + Field field = clazz.getDeclaredField(name); + field.setAccessible(true); + o = field.get(o); + } + return o; + } + + /** + * 得到所有定义字段 + */ + private void createExcelField() { + this.fields = getFields(); + this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList()); + this.maxHeight = getRowHeight(); + } + + /** + * 获取字段注解信息 + */ + public List getFields() { + List fields = new ArrayList(); + List tempFields = new ArrayList<>(); + tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields())); + tempFields.addAll(Arrays.asList(clazz.getDeclaredFields())); + for (Field field : tempFields) { + if (!ArrayUtils.contains(this.excludeFields, field.getName())) { + // 单注解 + if (field.isAnnotationPresent(Excel.class)) { + Excel attr = field.getAnnotation(Excel.class); + if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) { + field.setAccessible(true); + fields.add(new Object[]{field, attr}); + } + if (Collection.class.isAssignableFrom(field.getType())) { + subMethod = getSubMethod(field.getName(), clazz); + ParameterizedType pt = (ParameterizedType) field.getGenericType(); + Class subClass = (Class) pt.getActualTypeArguments()[0]; + this.subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class); + } + } + + // 多注解 + if (field.isAnnotationPresent(Excels.class)) { + Excels attrs = field.getAnnotation(Excels.class); + Excel[] excels = attrs.value(); + for (Excel attr : excels) { + if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) { + field.setAccessible(true); + fields.add(new Object[]{field, attr}); + } + } + } + } + } + return fields; + } + + /** + * 根据注解获取最大行高 + */ + public short getRowHeight() { + double maxHeight = 0; + for (Object[] os : this.fields) { + Excel excel = (Excel) os[1]; + maxHeight = Math.max(maxHeight, excel.height()); + } + return (short) (maxHeight * 20); + } + + /** + * 创建一个工作簿 + */ + public void createWorkbook() { + this.wb = new SXSSFWorkbook(500); + this.sheet = wb.createSheet(); + wb.setSheetName(0, sheetName); + this.styles = createStyles(wb); + } + + /** + * 创建工作表 + * + * @param sheetNo sheet数量 + * @param index 序号 + */ + public void createSheet(int sheetNo, int index) { + // 设置工作表的名称. + if (sheetNo > 1 && index > 0) { + this.sheet = wb.createSheet(); + this.createTitle(); + wb.setSheetName(index, sheetName + index); + } + } + + /** + * 获取单元格值 + * + * @param row 获取的行 + * @param column 获取单元格列号 + * @return 单元格值 + */ + public Object getCellValue(Row row, int column) { + if (row == null) { + return row; + } + Object val = ""; + try { + Cell cell = row.getCell(column); + if (StringUtils.isNotNull(cell)) { + if (cell.getCellType() == CellType.NUMERIC || cell.getCellType() == CellType.FORMULA) { + val = cell.getNumericCellValue(); + if (DateUtil.isCellDateFormatted(cell)) { + val = DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换 + } else { + if ((Double) val % 1 != 0) { + val = new BigDecimal(val.toString()); + } else { + val = new DecimalFormat("0").format(val); + } + } + } else if (cell.getCellType() == CellType.STRING) { + val = cell.getStringCellValue(); + } else if (cell.getCellType() == CellType.BOOLEAN) { + val = cell.getBooleanCellValue(); + } else if (cell.getCellType() == CellType.ERROR) { + val = cell.getErrorCellValue(); + } + + } + } catch (Exception e) { + return val; + } + return val; + } + + /** + * 判断是否是空行 + * + * @param row 判断的行 + * @return + */ + private boolean isRowEmpty(Row row) { + if (row == null) { + return true; + } + for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) { + Cell cell = row.getCell(i); + if (cell != null && cell.getCellType() != CellType.BLANK) { + return false; + } + } + return true; + } + + /** + * 获取Excel2003图片 + * + * @param sheet 当前sheet对象 + * @param workbook 工作簿对象 + * @return Map key:图片单元格索引(1_1)String,value:图片流PictureData + */ + public static Map getSheetPictures03(HSSFSheet sheet, HSSFWorkbook workbook) { + Map sheetIndexPicMap = new HashMap(); + List pictures = workbook.getAllPictures(); + if (!pictures.isEmpty()) { + for (HSSFShape shape : sheet.getDrawingPatriarch().getChildren()) { + HSSFClientAnchor anchor = (HSSFClientAnchor) shape.getAnchor(); + if (shape instanceof HSSFPicture) { + HSSFPicture pic = (HSSFPicture) shape; + int pictureIndex = pic.getPictureIndex() - 1; + HSSFPictureData picData = pictures.get(pictureIndex); + String picIndex = String.valueOf(anchor.getRow1()) + "_" + String.valueOf(anchor.getCol1()); + sheetIndexPicMap.put(picIndex, picData); + } + } + return sheetIndexPicMap; + } else { + return sheetIndexPicMap; + } + } + + /** + * 获取Excel2007图片 + * + * @param sheet 当前sheet对象 + * @param workbook 工作簿对象 + * @return Map key:图片单元格索引(1_1)String,value:图片流PictureData + */ + public static Map getSheetPictures07(XSSFSheet sheet, XSSFWorkbook workbook) { + Map sheetIndexPicMap = new HashMap(); + for (POIXMLDocumentPart dr : sheet.getRelations()) { + if (dr instanceof XSSFDrawing) { + XSSFDrawing drawing = (XSSFDrawing) dr; + List shapes = drawing.getShapes(); + for (XSSFShape shape : shapes) { + if (shape instanceof XSSFPicture) { + XSSFPicture pic = (XSSFPicture) shape; + XSSFClientAnchor anchor = pic.getPreferredSize(); + CTMarker ctMarker = anchor.getFrom(); + String picIndex = ctMarker.getRow() + "_" + ctMarker.getCol(); + sheetIndexPicMap.put(picIndex, pic.getPictureData()); + } + } + } + } + return sheetIndexPicMap; + } + + /** + * 格式化不同类型的日期对象 + * + * @param dateFormat 日期格式 + * @param val 被格式化的日期对象 + * @return 格式化后的日期字符 + */ + public String parseDateToStr(String dateFormat, Object val) { + if (val == null) { + return ""; + } + String str; + if (val instanceof Date) { + str = DateUtils.parseDateToStr(dateFormat, (Date) val); + } else if (val instanceof LocalDateTime) { + str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDateTime) val)); + } else if (val instanceof LocalDate) { + str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDate) val)); + } else { + str = val.toString(); + } + return str; + } + + /** + * 是否有对象的子列表 + */ + public boolean isSubList() { + return StringUtils.isNotNull(subFields) && subFields.size() > 0; + } + + /** + * 是否有对象的子列表,集合不为空 + */ + public boolean isSubListValue(T vo) { + return StringUtils.isNotNull(subFields) && subFields.size() > 0 && StringUtils.isNotNull(getListCellValue(vo)) && getListCellValue(vo).size() > 0; + } + + /** + * 获取集合的值 + */ + public Collection getListCellValue(Object obj) { + Object value; + try { + value = subMethod.invoke(obj, new Object[]{}); + } catch (Exception e) { + return new ArrayList(); + } + return (Collection) value; + } + + /** + * 获取对象的子列表方法 + * + * @param name 名称 + * @param pojoClass 类对象 + * @return 子列表方法 + */ + public Method getSubMethod(String name, Class pojoClass) { + StringBuffer getMethodName = new StringBuffer("get"); + getMethodName.append(name.substring(0, 1).toUpperCase()); + getMethodName.append(name.substring(1)); + Method method = null; + try { + method = pojoClass.getMethod(getMethodName.toString(), new Class[]{}); + } catch (Exception e) { + log.error("获取对象异常{}", e.getMessage()); + } + return method; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java new file mode 100644 index 00000000..b19953e0 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java @@ -0,0 +1,410 @@ +package com.ruoyi.common.utils.reflect; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Date; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; +import org.apache.poi.ss.usermodel.DateUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.utils.DateUtils; + +/** + * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数. + * + * @author ruoyi + */ +@SuppressWarnings("rawtypes") +public class ReflectUtils +{ + private static final String SETTER_PREFIX = "set"; + + private static final String GETTER_PREFIX = "get"; + + private static final String CGLIB_CLASS_SEPARATOR = "$$"; + + private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class); + + /** + * 调用Getter方法. + * 支持多级,如:对象名.对象名.方法 + */ + @SuppressWarnings("unchecked") + public static E invokeGetter(Object obj, String propertyName) + { + Object object = obj; + for (String name : StringUtils.split(propertyName, ".")) + { + String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name); + object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); + } + return (E) object; + } + + /** + * 调用Setter方法, 仅匹配方法名。 + * 支持多级,如:对象名.对象名.方法 + */ + public static void invokeSetter(Object obj, String propertyName, E value) + { + Object object = obj; + String[] names = StringUtils.split(propertyName, "."); + for (int i = 0; i < names.length; i++) + { + if (i < names.length - 1) + { + String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]); + object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); + } + else + { + String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]); + invokeMethodByName(object, setterMethodName, new Object[] { value }); + } + } + } + + /** + * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数. + */ + @SuppressWarnings("unchecked") + public static E getFieldValue(final Object obj, final String fieldName) + { + Field field = getAccessibleField(obj, fieldName); + if (field == null) + { + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + return null; + } + E result = null; + try + { + result = (E) field.get(obj); + } + catch (IllegalAccessException e) + { + logger.error("不可能抛出的异常{}", e.getMessage()); + } + return result; + } + + /** + * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数. + */ + public static void setFieldValue(final Object obj, final String fieldName, final E value) + { + Field field = getAccessibleField(obj, fieldName); + if (field == null) + { + // throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + return; + } + try + { + field.set(obj, value); + } + catch (IllegalAccessException e) + { + logger.error("不可能抛出的异常: {}", e.getMessage()); + } + } + + /** + * 直接调用对象方法, 无视private/protected修饰符. + * 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用. + * 同时匹配方法名+参数类型, + */ + @SuppressWarnings("unchecked") + public static E invokeMethod(final Object obj, final String methodName, final Class[] parameterTypes, + final Object[] args) + { + if (obj == null || methodName == null) + { + return null; + } + Method method = getAccessibleMethod(obj, methodName, parameterTypes); + if (method == null) + { + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); + return null; + } + try + { + return (E) method.invoke(obj, args); + } + catch (Exception e) + { + String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; + throw convertReflectionExceptionToUnchecked(msg, e); + } + } + + /** + * 直接调用对象方法, 无视private/protected修饰符, + * 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用. + * 只匹配函数名,如果有多个同名函数调用第一个。 + */ + @SuppressWarnings("unchecked") + public static E invokeMethodByName(final Object obj, final String methodName, final Object[] args) + { + Method method = getAccessibleMethodByName(obj, methodName, args.length); + if (method == null) + { + // 如果为空不报错,直接返回空。 + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); + return null; + } + try + { + // 类型转换(将参数数据类型转换为目标方法参数类型) + Class[] cs = method.getParameterTypes(); + for (int i = 0; i < cs.length; i++) + { + if (args[i] != null && !args[i].getClass().equals(cs[i])) + { + if (cs[i] == String.class) + { + args[i] = Convert.toStr(args[i]); + if (StringUtils.endsWith((String) args[i], ".0")) + { + args[i] = StringUtils.substringBefore((String) args[i], ".0"); + } + } + else if (cs[i] == Integer.class) + { + args[i] = Convert.toInt(args[i]); + } + else if (cs[i] == Long.class) + { + args[i] = Convert.toLong(args[i]); + } + else if (cs[i] == Double.class) + { + args[i] = Convert.toDouble(args[i]); + } + else if (cs[i] == Float.class) + { + args[i] = Convert.toFloat(args[i]); + } + else if (cs[i] == Date.class) + { + if (args[i] instanceof String) + { + args[i] = DateUtils.parseDate(args[i]); + } + else + { + args[i] = DateUtil.getJavaDate((Double) args[i]); + } + } + else if (cs[i] == boolean.class || cs[i] == Boolean.class) + { + args[i] = Convert.toBool(args[i]); + } + } + } + return (E) method.invoke(obj, args); + } + catch (Exception e) + { + String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; + throw convertReflectionExceptionToUnchecked(msg, e); + } + } + + /** + * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + */ + public static Field getAccessibleField(final Object obj, final String fieldName) + { + // 为空不报错。直接返回 null + if (obj == null) + { + return null; + } + Validate.notBlank(fieldName, "fieldName can't be blank"); + for (Class superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) + { + try + { + Field field = superClass.getDeclaredField(fieldName); + makeAccessible(field); + return field; + } + catch (NoSuchFieldException e) + { + continue; + } + } + return null; + } + + /** + * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + * 匹配函数名+参数类型。 + * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) + */ + public static Method getAccessibleMethod(final Object obj, final String methodName, + final Class... parameterTypes) + { + // 为空不报错。直接返回 null + if (obj == null) + { + return null; + } + Validate.notBlank(methodName, "methodName can't be blank"); + for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) + { + try + { + Method method = searchType.getDeclaredMethod(methodName, parameterTypes); + makeAccessible(method); + return method; + } + catch (NoSuchMethodException e) + { + continue; + } + } + return null; + } + + /** + * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + * 只匹配函数名。 + * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) + */ + public static Method getAccessibleMethodByName(final Object obj, final String methodName, int argsNum) + { + // 为空不报错。直接返回 null + if (obj == null) + { + return null; + } + Validate.notBlank(methodName, "methodName can't be blank"); + for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) + { + Method[] methods = searchType.getDeclaredMethods(); + for (Method method : methods) + { + if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum) + { + makeAccessible(method); + return method; + } + } + } + return null; + } + + /** + * 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 + */ + public static void makeAccessible(Method method) + { + if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) + && !method.isAccessible()) + { + method.setAccessible(true); + } + } + + /** + * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 + */ + public static void makeAccessible(Field field) + { + if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) + || Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) + { + field.setAccessible(true); + } + } + + /** + * 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处 + * 如无法找到, 返回Object.class. + */ + @SuppressWarnings("unchecked") + public static Class getClassGenricType(final Class clazz) + { + return getClassGenricType(clazz, 0); + } + + /** + * 通过反射, 获得Class定义中声明的父类的泛型参数的类型. + * 如无法找到, 返回Object.class. + */ + public static Class getClassGenricType(final Class clazz, final int index) + { + Type genType = clazz.getGenericSuperclass(); + + if (!(genType instanceof ParameterizedType)) + { + logger.debug(clazz.getSimpleName() + "'s superclass not ParameterizedType"); + return Object.class; + } + + Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); + + if (index >= params.length || index < 0) + { + logger.debug("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: " + + params.length); + return Object.class; + } + if (!(params[index] instanceof Class)) + { + logger.debug(clazz.getSimpleName() + " not set the actual class on superclass generic parameter"); + return Object.class; + } + + return (Class) params[index]; + } + + public static Class getUserClass(Object instance) + { + if (instance == null) + { + throw new RuntimeException("Instance must not be null"); + } + Class clazz = instance.getClass(); + if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) + { + Class superClass = clazz.getSuperclass(); + if (superClass != null && !Object.class.equals(superClass)) + { + return superClass; + } + } + return clazz; + + } + + /** + * 将反射时的checked exception转换为unchecked exception. + */ + public static RuntimeException convertReflectionExceptionToUnchecked(String msg, Exception e) + { + if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException + || e instanceof NoSuchMethodException) + { + return new IllegalArgumentException(msg, e); + } + else if (e instanceof InvocationTargetException) + { + return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException()); + } + return new RuntimeException(msg, e); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Base64.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Base64.java new file mode 100644 index 00000000..ca1cd924 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Base64.java @@ -0,0 +1,291 @@ +package com.ruoyi.common.utils.sign; + +/** + * Base64工具类 + * + * @author ruoyi + */ +public final class Base64 +{ + static private final int BASELENGTH = 128; + static private final int LOOKUPLENGTH = 64; + static private final int TWENTYFOURBITGROUP = 24; + static private final int EIGHTBIT = 8; + static private final int SIXTEENBIT = 16; + static private final int FOURBYTE = 4; + static private final int SIGN = -128; + static private final char PAD = '='; + static final private byte[] base64Alphabet = new byte[BASELENGTH]; + static final private char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH]; + + static + { + for (int i = 0; i < BASELENGTH; ++i) + { + base64Alphabet[i] = -1; + } + for (int i = 'Z'; i >= 'A'; i--) + { + base64Alphabet[i] = (byte) (i - 'A'); + } + for (int i = 'z'; i >= 'a'; i--) + { + base64Alphabet[i] = (byte) (i - 'a' + 26); + } + + for (int i = '9'; i >= '0'; i--) + { + base64Alphabet[i] = (byte) (i - '0' + 52); + } + + base64Alphabet['+'] = 62; + base64Alphabet['/'] = 63; + + for (int i = 0; i <= 25; i++) + { + lookUpBase64Alphabet[i] = (char) ('A' + i); + } + + for (int i = 26, j = 0; i <= 51; i++, j++) + { + lookUpBase64Alphabet[i] = (char) ('a' + j); + } + + for (int i = 52, j = 0; i <= 61; i++, j++) + { + lookUpBase64Alphabet[i] = (char) ('0' + j); + } + lookUpBase64Alphabet[62] = (char) '+'; + lookUpBase64Alphabet[63] = (char) '/'; + } + + private static boolean isWhiteSpace(char octect) + { + return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9); + } + + private static boolean isPad(char octect) + { + return (octect == PAD); + } + + private static boolean isData(char octect) + { + return (octect < BASELENGTH && base64Alphabet[octect] != -1); + } + + /** + * Encodes hex octects into Base64 + * + * @param binaryData Array containing binaryData + * @return Encoded Base64 array + */ + public static String encode(byte[] binaryData) + { + if (binaryData == null) + { + return null; + } + + int lengthDataBits = binaryData.length * EIGHTBIT; + if (lengthDataBits == 0) + { + return ""; + } + + int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP; + int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP; + int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets; + char encodedData[] = null; + + encodedData = new char[numberQuartet * 4]; + + byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0; + + int encodedIndex = 0; + int dataIndex = 0; + + for (int i = 0; i < numberTriplets; i++) + { + b1 = binaryData[dataIndex++]; + b2 = binaryData[dataIndex++]; + b3 = binaryData[dataIndex++]; + + l = (byte) (b2 & 0x0f); + k = (byte) (b1 & 0x03); + + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); + byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc); + + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f]; + } + + // form integral number of 6-bit groups + if (fewerThan24bits == EIGHTBIT) + { + b1 = binaryData[dataIndex]; + k = (byte) (b1 & 0x03); + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4]; + encodedData[encodedIndex++] = PAD; + encodedData[encodedIndex++] = PAD; + } + else if (fewerThan24bits == SIXTEENBIT) + { + b1 = binaryData[dataIndex]; + b2 = binaryData[dataIndex + 1]; + l = (byte) (b2 & 0x0f); + k = (byte) (b1 & 0x03); + + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); + + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2]; + encodedData[encodedIndex++] = PAD; + } + return new String(encodedData); + } + + /** + * Decodes Base64 data into octects + * + * @param encoded string containing Base64 data + * @return Array containind decoded data. + */ + public static byte[] decode(String encoded) + { + if (encoded == null) + { + return null; + } + + char[] base64Data = encoded.toCharArray(); + // remove white spaces + int len = removeWhiteSpace(base64Data); + + if (len % FOURBYTE != 0) + { + return null;// should be divisible by four + } + + int numberQuadruple = (len / FOURBYTE); + + if (numberQuadruple == 0) + { + return new byte[0]; + } + + byte decodedData[] = null; + byte b1 = 0, b2 = 0, b3 = 0, b4 = 0; + char d1 = 0, d2 = 0, d3 = 0, d4 = 0; + + int i = 0; + int encodedIndex = 0; + int dataIndex = 0; + decodedData = new byte[(numberQuadruple) * 3]; + + for (; i < numberQuadruple - 1; i++) + { + + if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++])) + || !isData((d3 = base64Data[dataIndex++])) || !isData((d4 = base64Data[dataIndex++]))) + { + return null; + } // if found "no data" just return null + + b1 = base64Alphabet[d1]; + b2 = base64Alphabet[d2]; + b3 = base64Alphabet[d3]; + b4 = base64Alphabet[d4]; + + decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); + } + + if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))) + { + return null;// if found "no data" just return null + } + + b1 = base64Alphabet[d1]; + b2 = base64Alphabet[d2]; + + d3 = base64Data[dataIndex++]; + d4 = base64Data[dataIndex++]; + if (!isData((d3)) || !isData((d4))) + {// Check if they are PAD characters + if (isPad(d3) && isPad(d4)) + { + if ((b2 & 0xf) != 0)// last 4 bits should be zero + { + return null; + } + byte[] tmp = new byte[i * 3 + 1]; + System.arraycopy(decodedData, 0, tmp, 0, i * 3); + tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); + return tmp; + } + else if (!isPad(d3) && isPad(d4)) + { + b3 = base64Alphabet[d3]; + if ((b3 & 0x3) != 0)// last 2 bits should be zero + { + return null; + } + byte[] tmp = new byte[i * 3 + 2]; + System.arraycopy(decodedData, 0, tmp, 0, i * 3); + tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + return tmp; + } + else + { + return null; + } + } + else + { // No PAD e.g 3cQl + b3 = base64Alphabet[d3]; + b4 = base64Alphabet[d4]; + decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); + + } + return decodedData; + } + + /** + * remove WhiteSpace from MIME containing encoded Base64 data. + * + * @param data the byte array of base64 data (with WS) + * @return the new length + */ + private static int removeWhiteSpace(char[] data) + { + if (data == null) + { + return 0; + } + + // count characters that's not whitespace + int newSize = 0; + int len = data.length; + for (int i = 0; i < len; i++) + { + if (!isWhiteSpace(data[i])) + { + data[newSize++] = data[i]; + } + } + return newSize; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Md5Utils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Md5Utils.java new file mode 100644 index 00000000..c1c58dbc --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Md5Utils.java @@ -0,0 +1,67 @@ +package com.ruoyi.common.utils.sign; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Md5加密方法 + * + * @author ruoyi + */ +public class Md5Utils +{ + private static final Logger log = LoggerFactory.getLogger(Md5Utils.class); + + private static byte[] md5(String s) + { + MessageDigest algorithm; + try + { + algorithm = MessageDigest.getInstance("MD5"); + algorithm.reset(); + algorithm.update(s.getBytes("UTF-8")); + byte[] messageDigest = algorithm.digest(); + return messageDigest; + } + catch (Exception e) + { + log.error("MD5 Error...", e); + } + return null; + } + + private static final String toHex(byte hash[]) + { + if (hash == null) + { + return null; + } + StringBuffer buf = new StringBuffer(hash.length * 2); + int i; + + for (i = 0; i < hash.length; i++) + { + if ((hash[i] & 0xff) < 0x10) + { + buf.append("0"); + } + buf.append(Long.toString(hash[i] & 0xff, 16)); + } + return buf.toString(); + } + + public static String hash(String s) + { + try + { + return new String(toHex(md5(s)).getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8); + } + catch (Exception e) + { + log.error("not supported charset...{}", e); + return s; + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java new file mode 100644 index 00000000..f290ec37 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java @@ -0,0 +1,158 @@ +package com.ruoyi.common.utils.spring; + +import org.springframework.aop.framework.AopContext; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; +import com.ruoyi.common.utils.StringUtils; + +/** + * spring工具类 方便在非spring管理环境中获取bean + * + * @author ruoyi + */ +@Component +public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware +{ + /** Spring应用上下文环境 */ + private static ConfigurableListableBeanFactory beanFactory; + + private static ApplicationContext applicationContext; + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException + { + SpringUtils.beanFactory = beanFactory; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException + { + SpringUtils.applicationContext = applicationContext; + } + + /** + * 获取对象 + * + * @param name + * @return Object 一个以所给名字注册的bean的实例 + * @throws org.springframework.beans.BeansException + * + */ + @SuppressWarnings("unchecked") + public static T getBean(String name) throws BeansException + { + return (T) beanFactory.getBean(name); + } + + /** + * 获取类型为requiredType的对象 + * + * @param clz + * @return + * @throws org.springframework.beans.BeansException + * + */ + public static T getBean(Class clz) throws BeansException + { + T result = (T) beanFactory.getBean(clz); + return result; + } + + /** + * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true + * + * @param name + * @return boolean + */ + public static boolean containsBean(String name) + { + return beanFactory.containsBean(name); + } + + /** + * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) + * + * @param name + * @return boolean + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.isSingleton(name); + } + + /** + * @param name + * @return Class 注册对象的类型 + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static Class getType(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.getType(name); + } + + /** + * 如果给定的bean名字在bean定义中有别名,则返回这些别名 + * + * @param name + * @return + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static String[] getAliases(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.getAliases(name); + } + + /** + * 获取aop代理对象 + * + * @param invoker + * @return + */ + @SuppressWarnings("unchecked") + public static T getAopProxy(T invoker) + { + return (T) AopContext.currentProxy(); + } + + /** + * 获取当前的环境配置,无配置返回null + * + * @return 当前的环境配置 + */ + public static String[] getActiveProfiles() + { + return applicationContext.getEnvironment().getActiveProfiles(); + } + + /** + * 获取当前的环境配置,当有多个环境配置时,只获取第一个 + * + * @return 当前的环境配置 + */ + public static String getActiveProfile() + { + final String[] activeProfiles = getActiveProfiles(); + return StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null; + } + + /** + * 获取配置文件中的值 + * + * @param key 配置文件的key + * @return 当前的配置文件的值 + * + */ + public static String getRequiredProperty(String key) + { + return applicationContext.getEnvironment().getRequiredProperty(key); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java new file mode 100644 index 00000000..246a9cfc --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java @@ -0,0 +1,61 @@ +package com.ruoyi.common.utils.sql; + +import com.ruoyi.common.exception.UtilException; +import com.ruoyi.common.utils.StringUtils; + +/** + * sql操作工具类 + * + * @author ruoyi + */ +public class SqlUtil +{ + /** + * 定义常用的 sql关键字 + */ + public static String SQL_REGEX = "select |insert |delete |update |drop |count |exec |chr |mid |master |truncate |char |and |declare "; + + /** + * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序) + */ + public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+"; + + /** + * 检查字符,防止注入绕过 + */ + public static String escapeOrderBySql(String value) + { + if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) + { + throw new UtilException("参数不符合规范,不能进行查询"); + } + return value; + } + + /** + * 验证 order by 语法是否符合规范 + */ + public static boolean isValidOrderBySql(String value) + { + return value.matches(SQL_PATTERN); + } + + /** + * SQL关键字检查 + */ + public static void filterKeyword(String value) + { + if (StringUtils.isEmpty(value)) + { + return; + } + String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|"); + for (String sqlKeyword : sqlKeywords) + { + if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1) + { + throw new UtilException("参数存在SQL注入风险"); + } + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/IdUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/IdUtils.java new file mode 100644 index 00000000..2c844271 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/IdUtils.java @@ -0,0 +1,49 @@ +package com.ruoyi.common.utils.uuid; + +/** + * ID生成器工具类 + * + * @author ruoyi + */ +public class IdUtils +{ + /** + * 获取随机UUID + * + * @return 随机UUID + */ + public static String randomUUID() + { + return UUID.randomUUID().toString(); + } + + /** + * 简化的UUID,去掉了横线 + * + * @return 简化的UUID,去掉了横线 + */ + public static String simpleUUID() + { + return UUID.randomUUID().toString(true); + } + + /** + * 获取随机UUID,使用性能更好的ThreadLocalRandom生成UUID + * + * @return 随机UUID + */ + public static String fastUUID() + { + return UUID.fastUUID().toString(); + } + + /** + * 简化的UUID,去掉了横线,使用性能更好的ThreadLocalRandom生成UUID + * + * @return 简化的UUID,去掉了横线 + */ + public static String fastSimpleUUID() + { + return UUID.fastUUID().toString(true); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/Seq.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/Seq.java new file mode 100644 index 00000000..528f3c1b --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/Seq.java @@ -0,0 +1,86 @@ +package com.ruoyi.common.utils.uuid; + +import java.util.concurrent.atomic.AtomicInteger; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; + +/** + * @author ruoyi 序列生成类 + */ +public class Seq +{ + // 通用序列类型 + public static final String commSeqType = "COMMON"; + + // 上传序列类型 + public static final String uploadSeqType = "UPLOAD"; + + // 通用接口序列数 + private static AtomicInteger commSeq = new AtomicInteger(1); + + // 上传接口序列数 + private static AtomicInteger uploadSeq = new AtomicInteger(1); + + // 机器标识 + private static String machineCode = "A"; + + /** + * 获取通用序列号 + * + * @return 序列值 + */ + public static String getId() + { + return getId(commSeqType); + } + + /** + * 默认16位序列号 yyMMddHHmmss + 一位机器标识 + 3长度循环递增字符串 + * + * @return 序列值 + */ + public static String getId(String type) + { + AtomicInteger atomicInt = commSeq; + if (uploadSeqType.equals(type)) + { + atomicInt = uploadSeq; + } + return getId(atomicInt, 3); + } + + /** + * 通用接口序列号 yyMMddHHmmss + 一位机器标识 + length长度循环递增字符串 + * + * @param atomicInt 序列数 + * @param length 数值长度 + * @return 序列值 + */ + public static String getId(AtomicInteger atomicInt, int length) + { + String result = DateUtils.dateTimeNow(); + result += machineCode; + result += getSeq(atomicInt, length); + return result; + } + + /** + * 序列循环递增字符串[1, 10 的 (length)幂次方), 用0左补齐length位数 + * + * @return 序列值 + */ + private synchronized static String getSeq(AtomicInteger atomicInt, int length) + { + // 先取值再+1 + int value = atomicInt.getAndIncrement(); + + // 如果更新后值>=10 的 (length)幂次方则重置为1 + int maxSeq = (int) Math.pow(10, length); + if (atomicInt.get() >= maxSeq) + { + atomicInt.set(1); + } + // 转字符串,用0左补齐 + return StringUtils.padl(value, length); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/UUID.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/UUID.java new file mode 100644 index 00000000..dfda46cf --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/UUID.java @@ -0,0 +1,484 @@ +package com.ruoyi.common.utils.uuid; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; +import com.ruoyi.common.exception.UtilException; + +/** + * 提供通用唯一识别码(universally unique identifier)(UUID)实现 + * + * @author ruoyi + */ +public final class UUID implements java.io.Serializable, Comparable +{ + private static final long serialVersionUID = -1185015143654744140L; + + /** + * SecureRandom 的单例 + * + */ + private static class Holder + { + static final SecureRandom numberGenerator = getSecureRandom(); + } + + /** 此UUID的最高64有效位 */ + private final long mostSigBits; + + /** 此UUID的最低64有效位 */ + private final long leastSigBits; + + /** + * 私有构造 + * + * @param data 数据 + */ + private UUID(byte[] data) + { + long msb = 0; + long lsb = 0; + assert data.length == 16 : "data must be 16 bytes in length"; + for (int i = 0; i < 8; i++) + { + msb = (msb << 8) | (data[i] & 0xff); + } + for (int i = 8; i < 16; i++) + { + lsb = (lsb << 8) | (data[i] & 0xff); + } + this.mostSigBits = msb; + this.leastSigBits = lsb; + } + + /** + * 使用指定的数据构造新的 UUID。 + * + * @param mostSigBits 用于 {@code UUID} 的最高有效 64 位 + * @param leastSigBits 用于 {@code UUID} 的最低有效 64 位 + */ + public UUID(long mostSigBits, long leastSigBits) + { + this.mostSigBits = mostSigBits; + this.leastSigBits = leastSigBits; + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的本地线程伪随机数生成器生成该 UUID。 + * + * @return 随机生成的 {@code UUID} + */ + public static UUID fastUUID() + { + return randomUUID(false); + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 + * + * @return 随机生成的 {@code UUID} + */ + public static UUID randomUUID() + { + return randomUUID(true); + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 + * + * @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码,否则可以得到更好的性能 + * @return 随机生成的 {@code UUID} + */ + public static UUID randomUUID(boolean isSecure) + { + final Random ng = isSecure ? Holder.numberGenerator : getRandom(); + + byte[] randomBytes = new byte[16]; + ng.nextBytes(randomBytes); + randomBytes[6] &= 0x0f; /* clear version */ + randomBytes[6] |= 0x40; /* set to version 4 */ + randomBytes[8] &= 0x3f; /* clear variant */ + randomBytes[8] |= 0x80; /* set to IETF variant */ + return new UUID(randomBytes); + } + + /** + * 根据指定的字节数组获取类型 3(基于名称的)UUID 的静态工厂。 + * + * @param name 用于构造 UUID 的字节数组。 + * + * @return 根据指定数组生成的 {@code UUID} + */ + public static UUID nameUUIDFromBytes(byte[] name) + { + MessageDigest md; + try + { + md = MessageDigest.getInstance("MD5"); + } + catch (NoSuchAlgorithmException nsae) + { + throw new InternalError("MD5 not supported"); + } + byte[] md5Bytes = md.digest(name); + md5Bytes[6] &= 0x0f; /* clear version */ + md5Bytes[6] |= 0x30; /* set to version 3 */ + md5Bytes[8] &= 0x3f; /* clear variant */ + md5Bytes[8] |= 0x80; /* set to IETF variant */ + return new UUID(md5Bytes); + } + + /** + * 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。 + * + * @param name 指定 {@code UUID} 字符串 + * @return 具有指定值的 {@code UUID} + * @throws IllegalArgumentException 如果 name 与 {@link #toString} 中描述的字符串表示形式不符抛出此异常 + * + */ + public static UUID fromString(String name) + { + String[] components = name.split("-"); + if (components.length != 5) + { + throw new IllegalArgumentException("Invalid UUID string: " + name); + } + for (int i = 0; i < 5; i++) + { + components[i] = "0x" + components[i]; + } + + long mostSigBits = Long.decode(components[0]).longValue(); + mostSigBits <<= 16; + mostSigBits |= Long.decode(components[1]).longValue(); + mostSigBits <<= 16; + mostSigBits |= Long.decode(components[2]).longValue(); + + long leastSigBits = Long.decode(components[3]).longValue(); + leastSigBits <<= 48; + leastSigBits |= Long.decode(components[4]).longValue(); + + return new UUID(mostSigBits, leastSigBits); + } + + /** + * 返回此 UUID 的 128 位值中的最低有效 64 位。 + * + * @return 此 UUID 的 128 位值中的最低有效 64 位。 + */ + public long getLeastSignificantBits() + { + return leastSigBits; + } + + /** + * 返回此 UUID 的 128 位值中的最高有效 64 位。 + * + * @return 此 UUID 的 128 位值中最高有效 64 位。 + */ + public long getMostSignificantBits() + { + return mostSigBits; + } + + /** + * 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的。 + *

+ * 版本号具有以下含意: + *

    + *
  • 1 基于时间的 UUID + *
  • 2 DCE 安全 UUID + *
  • 3 基于名称的 UUID + *
  • 4 随机生成的 UUID + *
+ * + * @return 此 {@code UUID} 的版本号 + */ + public int version() + { + // Version is bits masked by 0x000000000000F000 in MS long + return (int) ((mostSigBits >> 12) & 0x0f); + } + + /** + * 与此 {@code UUID} 相关联的变体号。变体号描述 {@code UUID} 的布局。 + *

+ * 变体号具有以下含意: + *

    + *
  • 0 为 NCS 向后兼容保留 + *
  • 2 IETF RFC 4122(Leach-Salz), 用于此类 + *
  • 6 保留,微软向后兼容 + *
  • 7 保留供以后定义使用 + *
+ * + * @return 此 {@code UUID} 相关联的变体号 + */ + public int variant() + { + // This field is composed of a varying number of bits. + // 0 - - Reserved for NCS backward compatibility + // 1 0 - The IETF aka Leach-Salz variant (used by this class) + // 1 1 0 Reserved, Microsoft backward compatibility + // 1 1 1 Reserved for future definition. + return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63)); + } + + /** + * 与此 UUID 相关联的时间戳值。 + * + *

+ * 60 位的时间戳值根据此 {@code UUID} 的 time_low、time_mid 和 time_hi 字段构造。
+ * 所得到的时间戳以 100 毫微秒为单位,从 UTC(通用协调时间) 1582 年 10 月 15 日零时开始。 + * + *

+ * 时间戳值仅在在基于时间的 UUID(其 version 类型为 1)中才有意义。
+ * 如果此 {@code UUID} 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 + * + * @throws UnsupportedOperationException 如果此 {@code UUID} 不是 version 为 1 的 UUID。 + */ + public long timestamp() throws UnsupportedOperationException + { + checkTimeBase(); + return (mostSigBits & 0x0FFFL) << 48// + | ((mostSigBits >> 16) & 0x0FFFFL) << 32// + | mostSigBits >>> 32; + } + + /** + * 与此 UUID 相关联的时钟序列值。 + * + *

+ * 14 位的时钟序列值根据此 UUID 的 clock_seq 字段构造。clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性。 + *

+ * {@code clockSequence} 值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。 如果此 UUID 不是基于时间的 UUID,则此方法抛出 + * UnsupportedOperationException。 + * + * @return 此 {@code UUID} 的时钟序列 + * + * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 + */ + public int clockSequence() throws UnsupportedOperationException + { + checkTimeBase(); + return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48); + } + + /** + * 与此 UUID 相关的节点值。 + * + *

+ * 48 位的节点值根据此 UUID 的 node 字段构造。此字段旨在用于保存机器的 IEEE 802 地址,该地址用于生成此 UUID 以保证空间唯一性。 + *

+ * 节点值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。
+ * 如果此 UUID 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 + * + * @return 此 {@code UUID} 的节点值 + * + * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 + */ + public long node() throws UnsupportedOperationException + { + checkTimeBase(); + return leastSigBits & 0x0000FFFFFFFFFFFFL; + } + + /** + * 返回此{@code UUID} 的字符串表现形式。 + * + *

+ * UUID 的字符串表示形式由此 BNF 描述: + * + *

+     * {@code
+     * UUID                   = ----
+     * time_low               = 4*
+     * time_mid               = 2*
+     * time_high_and_version  = 2*
+     * variant_and_sequence   = 2*
+     * node                   = 6*
+     * hexOctet               = 
+     * hexDigit               = [0-9a-fA-F]
+     * }
+     * 
+ * + * + * + * @return 此{@code UUID} 的字符串表现形式 + * @see #toString(boolean) + */ + @Override + public String toString() + { + return toString(false); + } + + /** + * 返回此{@code UUID} 的字符串表现形式。 + * + *

+ * UUID 的字符串表示形式由此 BNF 描述: + * + *

+     * {@code
+     * UUID                   = ----
+     * time_low               = 4*
+     * time_mid               = 2*
+     * time_high_and_version  = 2*
+     * variant_and_sequence   = 2*
+     * node                   = 6*
+     * hexOctet               = 
+     * hexDigit               = [0-9a-fA-F]
+     * }
+     * 
+ * + * + * + * @param isSimple 是否简单模式,简单模式为不带'-'的UUID字符串 + * @return 此{@code UUID} 的字符串表现形式 + */ + public String toString(boolean isSimple) + { + final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36); + // time_low + builder.append(digits(mostSigBits >> 32, 8)); + if (!isSimple) + { + builder.append('-'); + } + // time_mid + builder.append(digits(mostSigBits >> 16, 4)); + if (!isSimple) + { + builder.append('-'); + } + // time_high_and_version + builder.append(digits(mostSigBits, 4)); + if (!isSimple) + { + builder.append('-'); + } + // variant_and_sequence + builder.append(digits(leastSigBits >> 48, 4)); + if (!isSimple) + { + builder.append('-'); + } + // node + builder.append(digits(leastSigBits, 12)); + + return builder.toString(); + } + + /** + * 返回此 UUID 的哈希码。 + * + * @return UUID 的哈希码值。 + */ + @Override + public int hashCode() + { + long hilo = mostSigBits ^ leastSigBits; + return ((int) (hilo >> 32)) ^ (int) hilo; + } + + /** + * 将此对象与指定对象比较。 + *

+ * 当且仅当参数不为 {@code null}、而是一个 UUID 对象、具有与此 UUID 相同的 varriant、包含相同的值(每一位均相同)时,结果才为 {@code true}。 + * + * @param obj 要与之比较的对象 + * + * @return 如果对象相同,则返回 {@code true};否则返回 {@code false} + */ + @Override + public boolean equals(Object obj) + { + if ((null == obj) || (obj.getClass() != UUID.class)) + { + return false; + } + UUID id = (UUID) obj; + return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits); + } + + // Comparison Operations + + /** + * 将此 UUID 与指定的 UUID 比较。 + * + *

+ * 如果两个 UUID 不同,且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段,则第一个 UUID 大于第二个 UUID。 + * + * @param val 与此 UUID 比较的 UUID + * + * @return 在此 UUID 小于、等于或大于 val 时,分别返回 -1、0 或 1。 + * + */ + @Override + public int compareTo(UUID val) + { + // The ordering is intentionally set up so that the UUIDs + // can simply be numerically compared as two numbers + return (this.mostSigBits < val.mostSigBits ? -1 : // + (this.mostSigBits > val.mostSigBits ? 1 : // + (this.leastSigBits < val.leastSigBits ? -1 : // + (this.leastSigBits > val.leastSigBits ? 1 : // + 0)))); + } + + // ------------------------------------------------------------------------------------------------------------------- + // Private method start + /** + * 返回指定数字对应的hex值 + * + * @param val 值 + * @param digits 位 + * @return 值 + */ + private static String digits(long val, int digits) + { + long hi = 1L << (digits * 4); + return Long.toHexString(hi | (val & (hi - 1))).substring(1); + } + + /** + * 检查是否为time-based版本UUID + */ + private void checkTimeBase() + { + if (version() != 1) + { + throw new UnsupportedOperationException("Not a time-based UUID"); + } + } + + /** + * 获取{@link SecureRandom},类提供加密的强随机数生成器 (RNG) + * + * @return {@link SecureRandom} + */ + public static SecureRandom getSecureRandom() + { + try + { + return SecureRandom.getInstance("SHA1PRNG"); + } + catch (NoSuchAlgorithmException e) + { + throw new UtilException(e); + } + } + + /** + * 获取随机数生成器对象
+ * ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争争夺。 + * + * @return {@link ThreadLocalRandom} + */ + public static ThreadLocalRandom getRandom() + { + return ThreadLocalRandom.current(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/xss/Xss.java b/ruoyi-common/src/main/java/com/ruoyi/common/xss/Xss.java new file mode 100644 index 00000000..7bfdf04b --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/xss/Xss.java @@ -0,0 +1,27 @@ +package com.ruoyi.common.xss; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自定义xss校验注解 + * + * @author ruoyi + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(value = { ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER }) +@Constraint(validatedBy = { XssValidator.class }) +public @interface Xss +{ + String message() + + default "不允许任何脚本运行"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/xss/XssValidator.java b/ruoyi-common/src/main/java/com/ruoyi/common/xss/XssValidator.java new file mode 100644 index 00000000..ed9ec1f5 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/xss/XssValidator.java @@ -0,0 +1,34 @@ +package com.ruoyi.common.xss; + +import com.ruoyi.common.utils.StringUtils; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 自定义xss校验注解实现 + * + * @author ruoyi + */ +public class XssValidator implements ConstraintValidator +{ + private static final String HTML_PATTERN = "<(\\S*?)[^>]*>.*?|<.*? />"; + + @Override + public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) + { + if (StringUtils.isBlank(value)) + { + return true; + } + return !containsHtml(value); + } + + public static boolean containsHtml(String value) + { + Pattern pattern = Pattern.compile(HTML_PATTERN); + Matcher matcher = pattern.matcher(value); + return matcher.matches(); + } +} \ No newline at end of file diff --git a/ruoyi-common/target/classes/com/ruoyi/common/annotation/Anonymous.class b/ruoyi-common/target/classes/com/ruoyi/common/annotation/Anonymous.class new file mode 100644 index 00000000..8ed89cda Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/annotation/Anonymous.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/annotation/DataScope.class b/ruoyi-common/target/classes/com/ruoyi/common/annotation/DataScope.class new file mode 100644 index 00000000..43210a55 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/annotation/DataScope.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/annotation/DataSource.class b/ruoyi-common/target/classes/com/ruoyi/common/annotation/DataSource.class new file mode 100644 index 00000000..ae50afc9 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/annotation/DataSource.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/annotation/DictFormat.class b/ruoyi-common/target/classes/com/ruoyi/common/annotation/DictFormat.class new file mode 100644 index 00000000..d2e32b68 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/annotation/DictFormat.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/annotation/Excel$ColumnType.class b/ruoyi-common/target/classes/com/ruoyi/common/annotation/Excel$ColumnType.class new file mode 100644 index 00000000..b55aa1f9 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/annotation/Excel$ColumnType.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/annotation/Excel$Type.class b/ruoyi-common/target/classes/com/ruoyi/common/annotation/Excel$Type.class new file mode 100644 index 00000000..f0724806 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/annotation/Excel$Type.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/annotation/Excel.class b/ruoyi-common/target/classes/com/ruoyi/common/annotation/Excel.class new file mode 100644 index 00000000..5b8689be Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/annotation/Excel.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/annotation/Excels.class b/ruoyi-common/target/classes/com/ruoyi/common/annotation/Excels.class new file mode 100644 index 00000000..fed578d4 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/annotation/Excels.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/annotation/Log.class b/ruoyi-common/target/classes/com/ruoyi/common/annotation/Log.class new file mode 100644 index 00000000..c762b72e Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/annotation/Log.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/annotation/RateLimiter.class b/ruoyi-common/target/classes/com/ruoyi/common/annotation/RateLimiter.class new file mode 100644 index 00000000..bed09b1c Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/annotation/RateLimiter.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/annotation/RepeatSubmit.class b/ruoyi-common/target/classes/com/ruoyi/common/annotation/RepeatSubmit.class new file mode 100644 index 00000000..471494f7 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/annotation/RepeatSubmit.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/annotation/ReportExcel.class b/ruoyi-common/target/classes/com/ruoyi/common/annotation/ReportExcel.class new file mode 100644 index 00000000..9ce16f8f Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/annotation/ReportExcel.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/config/RuoYiConfig.class b/ruoyi-common/target/classes/com/ruoyi/common/config/RuoYiConfig.class new file mode 100644 index 00000000..04acf13d Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/config/RuoYiConfig.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/constant/CacheConstants.class b/ruoyi-common/target/classes/com/ruoyi/common/constant/CacheConstants.class new file mode 100644 index 00000000..53e52386 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/constant/CacheConstants.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/constant/Constants.class b/ruoyi-common/target/classes/com/ruoyi/common/constant/Constants.class new file mode 100644 index 00000000..efe5fed1 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/constant/Constants.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/constant/ErrorCodeConstants.class b/ruoyi-common/target/classes/com/ruoyi/common/constant/ErrorCodeConstants.class new file mode 100644 index 00000000..f8754e5e Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/constant/ErrorCodeConstants.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/constant/GenConstants.class b/ruoyi-common/target/classes/com/ruoyi/common/constant/GenConstants.class new file mode 100644 index 00000000..82a4183e Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/constant/GenConstants.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/constant/HttpStatus.class b/ruoyi-common/target/classes/com/ruoyi/common/constant/HttpStatus.class new file mode 100644 index 00000000..3e48329e Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/constant/HttpStatus.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/constant/ScheduleConstants$Status.class b/ruoyi-common/target/classes/com/ruoyi/common/constant/ScheduleConstants$Status.class new file mode 100644 index 00000000..94a75c47 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/constant/ScheduleConstants$Status.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/constant/ScheduleConstants.class b/ruoyi-common/target/classes/com/ruoyi/common/constant/ScheduleConstants.class new file mode 100644 index 00000000..8ea01aa9 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/constant/ScheduleConstants.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/constant/SqlConstants.class b/ruoyi-common/target/classes/com/ruoyi/common/constant/SqlConstants.class new file mode 100644 index 00000000..048aa067 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/constant/SqlConstants.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/constant/SysErrorCodeConstants.class b/ruoyi-common/target/classes/com/ruoyi/common/constant/SysErrorCodeConstants.class new file mode 100644 index 00000000..e0e05585 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/constant/SysErrorCodeConstants.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/constant/TreeConstants.class b/ruoyi-common/target/classes/com/ruoyi/common/constant/TreeConstants.class new file mode 100644 index 00000000..532ec592 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/constant/TreeConstants.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/constant/UserConstants.class b/ruoyi-common/target/classes/com/ruoyi/common/constant/UserConstants.class new file mode 100644 index 00000000..768177d2 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/constant/UserConstants.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/convert/DictConvert.class b/ruoyi-common/target/classes/com/ruoyi/common/convert/DictConvert.class new file mode 100644 index 00000000..59cc5ed0 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/convert/DictConvert.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/convert/JsonConvert.class b/ruoyi-common/target/classes/com/ruoyi/common/convert/JsonConvert.class new file mode 100644 index 00000000..514c120f Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/convert/JsonConvert.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/core/controller/BaseController$1.class b/ruoyi-common/target/classes/com/ruoyi/common/core/controller/BaseController$1.class new file mode 100644 index 00000000..44e16498 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/core/controller/BaseController$1.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/core/controller/BaseController.class b/ruoyi-common/target/classes/com/ruoyi/common/core/controller/BaseController.class new file mode 100644 index 00000000..aeb3b0a1 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/core/controller/BaseController.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/core/domain/AjaxResult.class b/ruoyi-common/target/classes/com/ruoyi/common/core/domain/AjaxResult.class new file mode 100644 index 00000000..5850fac9 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/core/domain/AjaxResult.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/core/domain/BaseEntity.class b/ruoyi-common/target/classes/com/ruoyi/common/core/domain/BaseEntity.class new file mode 100644 index 00000000..a59632b4 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/core/domain/BaseEntity.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/core/domain/DictTreeEntity.class b/ruoyi-common/target/classes/com/ruoyi/common/core/domain/DictTreeEntity.class new file mode 100644 index 00000000..5b1179a7 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/core/domain/DictTreeEntity.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/core/domain/R.class b/ruoyi-common/target/classes/com/ruoyi/common/core/domain/R.class new file mode 100644 index 00000000..55b99095 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/core/domain/R.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/core/domain/TenantBaseDO.class b/ruoyi-common/target/classes/com/ruoyi/common/core/domain/TenantBaseDO.class new file mode 100644 index 00000000..83384565 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/core/domain/TenantBaseDO.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/core/domain/TreeEntity.class b/ruoyi-common/target/classes/com/ruoyi/common/core/domain/TreeEntity.class new file mode 100644 index 00000000..bef2aee7 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/core/domain/TreeEntity.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/core/domain/TreeSelect.class b/ruoyi-common/target/classes/com/ruoyi/common/core/domain/TreeSelect.class new file mode 100644 index 00000000..91db6a14 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/core/domain/TreeSelect.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysDept.class b/ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysDept.class new file mode 100644 index 00000000..4602bf58 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysDept.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysDictData.class b/ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysDictData.class new file mode 100644 index 00000000..0cc9231c Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysDictData.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysDictType.class b/ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysDictType.class new file mode 100644 index 00000000..02a15195 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysDictType.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysMenu.class b/ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysMenu.class new file mode 100644 index 00000000..1a8b719e Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysMenu.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysRole.class b/ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysRole.class new file mode 100644 index 00000000..fcf76eb5 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysRole.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysUser.class b/ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysUser.class new file mode 100644 index 00000000..0132080f Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysUser.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/core/domain/model/LoginBody.class b/ruoyi-common/target/classes/com/ruoyi/common/core/domain/model/LoginBody.class new file mode 100644 index 00000000..541f90ed Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/core/domain/model/LoginBody.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/core/domain/model/LoginUser.class b/ruoyi-common/target/classes/com/ruoyi/common/core/domain/model/LoginUser.class new file mode 100644 index 00000000..13791ca5 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/core/domain/model/LoginUser.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/core/domain/model/RegisterBody.class b/ruoyi-common/target/classes/com/ruoyi/common/core/domain/model/RegisterBody.class new file mode 100644 index 00000000..e4e717e1 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/core/domain/model/RegisterBody.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/core/page/PageDomain.class b/ruoyi-common/target/classes/com/ruoyi/common/core/page/PageDomain.class new file mode 100644 index 00000000..b4530eb6 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/core/page/PageDomain.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/core/page/TableDataInfo.class b/ruoyi-common/target/classes/com/ruoyi/common/core/page/TableDataInfo.class new file mode 100644 index 00000000..977ed1ef Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/core/page/TableDataInfo.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/core/page/TableSupport.class b/ruoyi-common/target/classes/com/ruoyi/common/core/page/TableSupport.class new file mode 100644 index 00000000..51c135d2 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/core/page/TableSupport.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/core/text/CharsetKit.class b/ruoyi-common/target/classes/com/ruoyi/common/core/text/CharsetKit.class new file mode 100644 index 00000000..06176e59 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/core/text/CharsetKit.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/core/text/Convert.class b/ruoyi-common/target/classes/com/ruoyi/common/core/text/Convert.class new file mode 100644 index 00000000..c3f2c90a Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/core/text/Convert.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/core/text/StrFormatter.class b/ruoyi-common/target/classes/com/ruoyi/common/core/text/StrFormatter.class new file mode 100644 index 00000000..d0c17369 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/core/text/StrFormatter.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/enums/BusinessStatus.class b/ruoyi-common/target/classes/com/ruoyi/common/enums/BusinessStatus.class new file mode 100644 index 00000000..d587b37d Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/enums/BusinessStatus.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/enums/BusinessType.class b/ruoyi-common/target/classes/com/ruoyi/common/enums/BusinessType.class new file mode 100644 index 00000000..3d39fac2 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/enums/BusinessType.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/enums/CommonStatusEnum.class b/ruoyi-common/target/classes/com/ruoyi/common/enums/CommonStatusEnum.class new file mode 100644 index 00000000..beaecc9e Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/enums/CommonStatusEnum.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/enums/DataSourceType.class b/ruoyi-common/target/classes/com/ruoyi/common/enums/DataSourceType.class new file mode 100644 index 00000000..ef976f48 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/enums/DataSourceType.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/enums/HttpMethod.class b/ruoyi-common/target/classes/com/ruoyi/common/enums/HttpMethod.class new file mode 100644 index 00000000..11f5c905 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/enums/HttpMethod.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/enums/LimitType.class b/ruoyi-common/target/classes/com/ruoyi/common/enums/LimitType.class new file mode 100644 index 00000000..de21e9f5 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/enums/LimitType.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/enums/OperatorType.class b/ruoyi-common/target/classes/com/ruoyi/common/enums/OperatorType.class new file mode 100644 index 00000000..f6a702fa Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/enums/OperatorType.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/enums/RoleCodeEnum.class b/ruoyi-common/target/classes/com/ruoyi/common/enums/RoleCodeEnum.class new file mode 100644 index 00000000..5ba1e0dc Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/enums/RoleCodeEnum.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/enums/UserStatus.class b/ruoyi-common/target/classes/com/ruoyi/common/enums/UserStatus.class new file mode 100644 index 00000000..40fa9506 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/enums/UserStatus.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/enums/bs/DeviceStatusEnum.class b/ruoyi-common/target/classes/com/ruoyi/common/enums/bs/DeviceStatusEnum.class new file mode 100644 index 00000000..979f271e Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/enums/bs/DeviceStatusEnum.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/enums/bs/TaskCodeEnum.class b/ruoyi-common/target/classes/com/ruoyi/common/enums/bs/TaskCodeEnum.class new file mode 100644 index 00000000..9b3ccd1f Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/enums/bs/TaskCodeEnum.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/enums/bs/TaskExceptionStatusEnum.class b/ruoyi-common/target/classes/com/ruoyi/common/enums/bs/TaskExceptionStatusEnum.class new file mode 100644 index 00000000..a698f4e5 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/enums/bs/TaskExceptionStatusEnum.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/enums/bs/TaskSettingStatusEnum.class b/ruoyi-common/target/classes/com/ruoyi/common/enums/bs/TaskSettingStatusEnum.class new file mode 100644 index 00000000..67e9a1cb Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/enums/bs/TaskSettingStatusEnum.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/enums/bs/TaskTypeEnum.class b/ruoyi-common/target/classes/com/ruoyi/common/enums/bs/TaskTypeEnum.class new file mode 100644 index 00000000..f477905c Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/enums/bs/TaskTypeEnum.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/exception/DemoModeException.class b/ruoyi-common/target/classes/com/ruoyi/common/exception/DemoModeException.class new file mode 100644 index 00000000..c2c7f3e6 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/exception/DemoModeException.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/exception/ErrorCode.class b/ruoyi-common/target/classes/com/ruoyi/common/exception/ErrorCode.class new file mode 100644 index 00000000..990604a2 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/exception/ErrorCode.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/exception/GlobalException.class b/ruoyi-common/target/classes/com/ruoyi/common/exception/GlobalException.class new file mode 100644 index 00000000..a1a03aeb Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/exception/GlobalException.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/exception/ServiceException.class b/ruoyi-common/target/classes/com/ruoyi/common/exception/ServiceException.class new file mode 100644 index 00000000..a798c4c9 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/exception/ServiceException.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/exception/UtilException.class b/ruoyi-common/target/classes/com/ruoyi/common/exception/UtilException.class new file mode 100644 index 00000000..fd581169 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/exception/UtilException.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/exception/base/BaseException.class b/ruoyi-common/target/classes/com/ruoyi/common/exception/base/BaseException.class new file mode 100644 index 00000000..55bd58a8 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/exception/base/BaseException.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/exception/file/FileException.class b/ruoyi-common/target/classes/com/ruoyi/common/exception/file/FileException.class new file mode 100644 index 00000000..7c9cb9eb Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/exception/file/FileException.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.class b/ruoyi-common/target/classes/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.class new file mode 100644 index 00000000..418d10ed Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/exception/file/FileSizeLimitExceededException.class b/ruoyi-common/target/classes/com/ruoyi/common/exception/file/FileSizeLimitExceededException.class new file mode 100644 index 00000000..867ca4e8 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/exception/file/FileSizeLimitExceededException.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/exception/file/InvalidExtensionException$InvalidFlashExtensionException.class b/ruoyi-common/target/classes/com/ruoyi/common/exception/file/InvalidExtensionException$InvalidFlashExtensionException.class new file mode 100644 index 00000000..c52a5b02 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/exception/file/InvalidExtensionException$InvalidFlashExtensionException.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/exception/file/InvalidExtensionException$InvalidImageExtensionException.class b/ruoyi-common/target/classes/com/ruoyi/common/exception/file/InvalidExtensionException$InvalidImageExtensionException.class new file mode 100644 index 00000000..bb4ba719 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/exception/file/InvalidExtensionException$InvalidImageExtensionException.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/exception/file/InvalidExtensionException$InvalidMediaExtensionException.class b/ruoyi-common/target/classes/com/ruoyi/common/exception/file/InvalidExtensionException$InvalidMediaExtensionException.class new file mode 100644 index 00000000..abf13a08 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/exception/file/InvalidExtensionException$InvalidMediaExtensionException.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/exception/file/InvalidExtensionException$InvalidVideoExtensionException.class b/ruoyi-common/target/classes/com/ruoyi/common/exception/file/InvalidExtensionException$InvalidVideoExtensionException.class new file mode 100644 index 00000000..10f91b3d Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/exception/file/InvalidExtensionException$InvalidVideoExtensionException.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/exception/file/InvalidExtensionException.class b/ruoyi-common/target/classes/com/ruoyi/common/exception/file/InvalidExtensionException.class new file mode 100644 index 00000000..43ee6a96 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/exception/file/InvalidExtensionException.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/exception/job/TaskException$Code.class b/ruoyi-common/target/classes/com/ruoyi/common/exception/job/TaskException$Code.class new file mode 100644 index 00000000..40533630 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/exception/job/TaskException$Code.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/exception/job/TaskException.class b/ruoyi-common/target/classes/com/ruoyi/common/exception/job/TaskException.class new file mode 100644 index 00000000..fd1270be Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/exception/job/TaskException.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/exception/user/CaptchaException.class b/ruoyi-common/target/classes/com/ruoyi/common/exception/user/CaptchaException.class new file mode 100644 index 00000000..4efae4d0 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/exception/user/CaptchaException.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/exception/user/CaptchaExpireException.class b/ruoyi-common/target/classes/com/ruoyi/common/exception/user/CaptchaExpireException.class new file mode 100644 index 00000000..d0258147 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/exception/user/CaptchaExpireException.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/exception/user/UserException.class b/ruoyi-common/target/classes/com/ruoyi/common/exception/user/UserException.class new file mode 100644 index 00000000..ef0d6745 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/exception/user/UserException.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/exception/user/UserPasswordNotMatchException.class b/ruoyi-common/target/classes/com/ruoyi/common/exception/user/UserPasswordNotMatchException.class new file mode 100644 index 00000000..cd4e9b13 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/exception/user/UserPasswordNotMatchException.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.class b/ruoyi-common/target/classes/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.class new file mode 100644 index 00000000..2dd4989d Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/exception/util/ServiceExceptionUtil.class b/ruoyi-common/target/classes/com/ruoyi/common/exception/util/ServiceExceptionUtil.class new file mode 100644 index 00000000..66c2d963 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/exception/util/ServiceExceptionUtil.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/filter/ApiRequestFilter.class b/ruoyi-common/target/classes/com/ruoyi/common/filter/ApiRequestFilter.class new file mode 100644 index 00000000..c83ebd8e Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/filter/ApiRequestFilter.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/filter/PropertyPreExcludeFilter.class b/ruoyi-common/target/classes/com/ruoyi/common/filter/PropertyPreExcludeFilter.class new file mode 100644 index 00000000..a197dbb9 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/filter/PropertyPreExcludeFilter.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/filter/RepeatableFilter.class b/ruoyi-common/target/classes/com/ruoyi/common/filter/RepeatableFilter.class new file mode 100644 index 00000000..f85340da Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/filter/RepeatableFilter.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/filter/RepeatedlyRequestWrapper$1.class b/ruoyi-common/target/classes/com/ruoyi/common/filter/RepeatedlyRequestWrapper$1.class new file mode 100644 index 00000000..cd1c9050 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/filter/RepeatedlyRequestWrapper$1.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/filter/RepeatedlyRequestWrapper.class b/ruoyi-common/target/classes/com/ruoyi/common/filter/RepeatedlyRequestWrapper.class new file mode 100644 index 00000000..ca611323 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/filter/RepeatedlyRequestWrapper.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/filter/XssFilter.class b/ruoyi-common/target/classes/com/ruoyi/common/filter/XssFilter.class new file mode 100644 index 00000000..68c81edb Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/filter/XssFilter.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/filter/XssHttpServletRequestWrapper$1.class b/ruoyi-common/target/classes/com/ruoyi/common/filter/XssHttpServletRequestWrapper$1.class new file mode 100644 index 00000000..b5ae9b1d Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/filter/XssHttpServletRequestWrapper$1.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/filter/XssHttpServletRequestWrapper.class b/ruoyi-common/target/classes/com/ruoyi/common/filter/XssHttpServletRequestWrapper.class new file mode 100644 index 00000000..270adc32 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/filter/XssHttpServletRequestWrapper.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/handler/JsonLongSetTypeHandler$1.class b/ruoyi-common/target/classes/com/ruoyi/common/handler/JsonLongSetTypeHandler$1.class new file mode 100644 index 00000000..6b0964b1 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/handler/JsonLongSetTypeHandler$1.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/handler/JsonLongSetTypeHandler.class b/ruoyi-common/target/classes/com/ruoyi/common/handler/JsonLongSetTypeHandler.class new file mode 100644 index 00000000..1fcbefe2 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/handler/JsonLongSetTypeHandler.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/mybatis/dataobject/BaseDO.class b/ruoyi-common/target/classes/com/ruoyi/common/mybatis/dataobject/BaseDO.class new file mode 100644 index 00000000..363461b2 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/mybatis/dataobject/BaseDO.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/mybatis/handler/DefaultDBFieldHandler.class b/ruoyi-common/target/classes/com/ruoyi/common/mybatis/handler/DefaultDBFieldHandler.class new file mode 100644 index 00000000..b03bc155 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/mybatis/handler/DefaultDBFieldHandler.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/mybatis/mapper/BaseMapperX.class b/ruoyi-common/target/classes/com/ruoyi/common/mybatis/mapper/BaseMapperX.class new file mode 100644 index 00000000..a66544eb Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/mybatis/mapper/BaseMapperX.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/mybatis/pojo/PageParam.class b/ruoyi-common/target/classes/com/ruoyi/common/mybatis/pojo/PageParam.class new file mode 100644 index 00000000..35e51c50 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/mybatis/pojo/PageParam.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/mybatis/pojo/PageResult.class b/ruoyi-common/target/classes/com/ruoyi/common/mybatis/pojo/PageResult.class new file mode 100644 index 00000000..510e9b35 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/mybatis/pojo/PageResult.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/mybatis/pojo/SortingField.class b/ruoyi-common/target/classes/com/ruoyi/common/mybatis/pojo/SortingField.class new file mode 100644 index 00000000..356a0463 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/mybatis/pojo/SortingField.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/mybatis/query/LambdaQueryWrapperX.class b/ruoyi-common/target/classes/com/ruoyi/common/mybatis/query/LambdaQueryWrapperX.class new file mode 100644 index 00000000..33919054 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/mybatis/query/LambdaQueryWrapperX.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/mybatis/query/QueryWrapperX.class b/ruoyi-common/target/classes/com/ruoyi/common/mybatis/query/QueryWrapperX.class new file mode 100644 index 00000000..22c24719 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/mybatis/query/QueryWrapperX.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/mybatis/util/JdbcUtils.class b/ruoyi-common/target/classes/com/ruoyi/common/mybatis/util/JdbcUtils.class new file mode 100644 index 00000000..dc73cffd Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/mybatis/util/JdbcUtils.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/mybatis/util/MyBatisUtils.class b/ruoyi-common/target/classes/com/ruoyi/common/mybatis/util/MyBatisUtils.class new file mode 100644 index 00000000..6d94a0a7 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/mybatis/util/MyBatisUtils.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/Arith.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/Arith.class new file mode 100644 index 00000000..93c58578 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/Arith.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/DateUtils.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/DateUtils.class new file mode 100644 index 00000000..3010a99b Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/DateUtils.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/DictUtils.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/DictUtils.class new file mode 100644 index 00000000..eac8ccbe Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/DictUtils.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/EhcacheUtil.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/EhcacheUtil.class new file mode 100644 index 00000000..40ae349f Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/EhcacheUtil.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/ExcelUtils.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/ExcelUtils.class new file mode 100644 index 00000000..eb0ecde1 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/ExcelUtils.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/ExceptionUtil.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/ExceptionUtil.class new file mode 100644 index 00000000..cd62101e Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/ExceptionUtil.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/JsonUtils.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/JsonUtils.class new file mode 100644 index 00000000..3ad60a16 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/JsonUtils.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/LogUtils.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/LogUtils.class new file mode 100644 index 00000000..c798f817 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/LogUtils.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/MessageUtils.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/MessageUtils.class new file mode 100644 index 00000000..ca75c7cf Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/MessageUtils.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/NumberUtils.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/NumberUtils.class new file mode 100644 index 00000000..37c392e4 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/NumberUtils.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/ObjectUtils.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/ObjectUtils.class new file mode 100644 index 00000000..50741c33 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/ObjectUtils.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/PageUtils.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/PageUtils.class new file mode 100644 index 00000000..02dd97f0 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/PageUtils.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/SecurityUtils.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/SecurityUtils.class new file mode 100644 index 00000000..6b9fb167 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/SecurityUtils.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/ServletUtils.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/ServletUtils.class new file mode 100644 index 00000000..1905c835 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/ServletUtils.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/StringUtils.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/StringUtils.class new file mode 100644 index 00000000..ea2b8546 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/StringUtils.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/Threads.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/Threads.class new file mode 100644 index 00000000..ef07bbe1 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/Threads.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/TreeUtils.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/TreeUtils.class new file mode 100644 index 00000000..0b9bcff9 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/TreeUtils.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/ValidationUtils.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/ValidationUtils.class new file mode 100644 index 00000000..97524d81 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/ValidationUtils.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/bean/BeanUtils.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/bean/BeanUtils.class new file mode 100644 index 00000000..73eff3f2 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/bean/BeanUtils.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/bean/BeanValidators.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/bean/BeanValidators.class new file mode 100644 index 00000000..89e8abf3 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/bean/BeanValidators.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/collection/ArrayUtils.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/collection/ArrayUtils.class new file mode 100644 index 00000000..86b0c3d0 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/collection/ArrayUtils.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/collection/CollectionUtils.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/collection/CollectionUtils.class new file mode 100644 index 00000000..535cfba2 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/collection/CollectionUtils.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/collection/IntArrayValuable.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/collection/IntArrayValuable.class new file mode 100644 index 00000000..da59a9cd Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/collection/IntArrayValuable.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/collection/KeyValue.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/collection/KeyValue.class new file mode 100644 index 00000000..4222a109 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/collection/KeyValue.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/collection/MapUtils.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/collection/MapUtils.class new file mode 100644 index 00000000..a95ddbc1 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/collection/MapUtils.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/collection/SetUtils.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/collection/SetUtils.class new file mode 100644 index 00000000..9551c97d Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/collection/SetUtils.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/file/FileTypeUtils.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/file/FileTypeUtils.class new file mode 100644 index 00000000..d78a82f4 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/file/FileTypeUtils.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/file/FileUploadUtils.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/file/FileUploadUtils.class new file mode 100644 index 00000000..92d46ace Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/file/FileUploadUtils.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/file/FileUtils.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/file/FileUtils.class new file mode 100644 index 00000000..547dce63 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/file/FileUtils.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/file/ImageUtils.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/file/ImageUtils.class new file mode 100644 index 00000000..d7e158e0 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/file/ImageUtils.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/file/IoUtils.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/file/IoUtils.class new file mode 100644 index 00000000..c6edb342 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/file/IoUtils.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/file/MimeTypeUtils.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/file/MimeTypeUtils.class new file mode 100644 index 00000000..3465723a Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/file/MimeTypeUtils.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/html/EscapeUtil.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/html/EscapeUtil.class new file mode 100644 index 00000000..1005c8df Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/html/EscapeUtil.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/html/HTMLFilter.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/html/HTMLFilter.class new file mode 100644 index 00000000..7fd65686 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/html/HTMLFilter.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/http/HttpHelper.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/http/HttpHelper.class new file mode 100644 index 00000000..6bef188c Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/http/HttpHelper.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/http/HttpUtils$1.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/http/HttpUtils$1.class new file mode 100644 index 00000000..3df88e35 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/http/HttpUtils$1.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/http/HttpUtils$TrustAnyHostnameVerifier.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/http/HttpUtils$TrustAnyHostnameVerifier.class new file mode 100644 index 00000000..533ae32e Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/http/HttpUtils$TrustAnyHostnameVerifier.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/http/HttpUtils$TrustAnyTrustManager.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/http/HttpUtils$TrustAnyTrustManager.class new file mode 100644 index 00000000..9777bc43 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/http/HttpUtils$TrustAnyTrustManager.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/http/HttpUtils.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/http/HttpUtils.class new file mode 100644 index 00000000..c3336646 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/http/HttpUtils.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/ip/AddressUtils.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/ip/AddressUtils.class new file mode 100644 index 00000000..9cdd6e72 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/ip/AddressUtils.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/ip/IpUtils.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/ip/IpUtils.class new file mode 100644 index 00000000..a3988230 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/ip/IpUtils.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/poi/ExcelHandlerAdapter.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/poi/ExcelHandlerAdapter.class new file mode 100644 index 00000000..9496da9d Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/poi/ExcelHandlerAdapter.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/poi/ExcelUtil.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/poi/ExcelUtil.class new file mode 100644 index 00000000..49f6982b Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/poi/ExcelUtil.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/reflect/ReflectUtils.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/reflect/ReflectUtils.class new file mode 100644 index 00000000..a1eb773b Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/reflect/ReflectUtils.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/sign/Base64.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/sign/Base64.class new file mode 100644 index 00000000..0d126049 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/sign/Base64.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/sign/Md5Utils.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/sign/Md5Utils.class new file mode 100644 index 00000000..d8cc407d Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/sign/Md5Utils.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/spring/SpringUtils.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/spring/SpringUtils.class new file mode 100644 index 00000000..75e11ade Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/spring/SpringUtils.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/sql/SqlUtil.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/sql/SqlUtil.class new file mode 100644 index 00000000..26050ee3 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/sql/SqlUtil.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/uuid/IdUtils.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/uuid/IdUtils.class new file mode 100644 index 00000000..60ed5df8 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/uuid/IdUtils.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/uuid/Seq.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/uuid/Seq.class new file mode 100644 index 00000000..028e4a78 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/uuid/Seq.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/uuid/UUID$Holder.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/uuid/UUID$Holder.class new file mode 100644 index 00000000..b9ef6faf Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/uuid/UUID$Holder.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/utils/uuid/UUID.class b/ruoyi-common/target/classes/com/ruoyi/common/utils/uuid/UUID.class new file mode 100644 index 00000000..fb526f05 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/utils/uuid/UUID.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/xss/Xss.class b/ruoyi-common/target/classes/com/ruoyi/common/xss/Xss.class new file mode 100644 index 00000000..404ec7a4 Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/xss/Xss.class differ diff --git a/ruoyi-common/target/classes/com/ruoyi/common/xss/XssValidator.class b/ruoyi-common/target/classes/com/ruoyi/common/xss/XssValidator.class new file mode 100644 index 00000000..fa07146d Binary files /dev/null and b/ruoyi-common/target/classes/com/ruoyi/common/xss/XssValidator.class differ diff --git a/ruoyi-flowable/README.md b/ruoyi-flowable/README.md new file mode 100644 index 00000000..289bb186 --- /dev/null +++ b/ruoyi-flowable/README.md @@ -0,0 +1,30 @@ +1、或签配置方式: +回路特性:并行多重事件 +元素变量:固定节点ID_assignee,意义不大,代码里强设了 +循环基数:固定1 +完成条件配置为:${nrOfCompletedInstances >= 1}。 +节点XML 示例: + + +${coll_userList.size()} +${nrOfCompletedInstances/nrOfInstances == 1} + + +配置截图示例: +![img_3.png](img_3.png) + +2、并签配置方式(没有业务,需后续验证): +回路特性:并行多重事件 +元素变量:固定节点ID_assignee,意义不大,代码里强设了 +循环基数:固定为${coll_userList.size()} +完成条件配置为:${nrOfCompletedInstances/nrOfInstances == 1}。 +`nrOfCompletedInstances` 和 `nrOfInstances` 是并行多实例活动(ParallelMultiInstanceActivity)中的两个内置变量。 +- `nrOfCompletedInstances`:表示已完成的实例数量。在并行多实例活动中,每当一个实例完成时,`nrOfCompletedInstances` 的值就会增加。 +- `nrOfInstances`:表示总实例数量。在并行多实例活动中,`nrOfInstances` 的值等于要创建的实例总数。 +节点XML示例: + + +${coll_userList.size()} +${nrOfCompletedInstances/nrOfInstances == 1} + + \ No newline at end of file diff --git a/ruoyi-flowable/img_3.png b/ruoyi-flowable/img_3.png new file mode 100644 index 00000000..90586941 Binary files /dev/null and b/ruoyi-flowable/img_3.png differ diff --git a/ruoyi-flowable/pom.xml b/ruoyi-flowable/pom.xml new file mode 100644 index 00000000..d6724642 --- /dev/null +++ b/ruoyi-flowable/pom.xml @@ -0,0 +1,63 @@ + + + + ruoyi + com.ruoyi + 3.8.4 + + 4.0.0 + + ruoyi-flowable + + + + + + xerces + xercesImpl + + + + com.ruoyi + ruoyi-common + + + + + com.ruoyi + ruoyi-system + + + + + cn.hutool + hutool-all + + + + + com.github.xiaoymin + knife4j-spring-boot-starter + + + + + org.projectlombok + lombok + true + + + + + org.flowable + flowable-spring-boot-starter-process + + + org.flowable + flowable-spring-boot-starter-actuator + + + + diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/config/FlowableConfiguration.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/config/FlowableConfiguration.java new file mode 100644 index 00000000..427aea9d --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/config/FlowableConfiguration.java @@ -0,0 +1,44 @@ +package com.ruoyi.flowable.config; + +import com.ruoyi.flowable.core.web.FlowableWebFilter; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.task.AsyncListenableTaskExecutor; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +@Configuration +public class FlowableConfiguration { + + /** + * 参考 {@link org.flowable.spring.boot.FlowableJobConfiguration} 类,创建对应的 AsyncListenableTaskExecutor Bean + * + * 如果不创建,会导致项目启动时,Flowable 报错的问题 + */ + @Bean + public AsyncListenableTaskExecutor taskExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(8); + executor.setMaxPoolSize(8); + executor.setQueueCapacity(100); + executor.setThreadNamePrefix("flowable-task-Executor-"); + executor.setAwaitTerminationSeconds(30); + executor.setWaitForTasksToCompleteOnShutdown(true); + executor.setAllowCoreThreadTimeOut(true); + executor.initialize(); + return executor; + } + + /** + * 配置 flowable Web 过滤器 + */ + @Bean + public FilterRegistrationBean flowableWebFilter() { + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + registrationBean.setFilter(new FlowableWebFilter()); + // Spring Security Filter 默认为 -100,可见 org.springframework.boot.autoconfigure.security.SecurityProperties 配置属性类 + // 需要保证在 Spring Security 过滤后面 + registrationBean.setOrder(Integer.MAX_VALUE); + return registrationBean; + } +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/controller/definition/BpmFormController.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/controller/definition/BpmFormController.java new file mode 100644 index 00000000..e1c06ba7 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/controller/definition/BpmFormController.java @@ -0,0 +1,81 @@ +package com.ruoyi.flowable.controller.definition; + +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.flowable.convert.definition.BpmFormConvert; +import com.ruoyi.flowable.domain.entity.definition.BpmFormDO; +import com.ruoyi.flowable.domain.vo.form.BpmFormCreateReqVO; +import com.ruoyi.flowable.domain.vo.form.BpmFormPageReqVO; +import com.ruoyi.flowable.domain.vo.form.BpmFormRespVO; +import com.ruoyi.flowable.domain.vo.form.BpmFormUpdateReqVO; +import com.ruoyi.flowable.service.definition.BpmFormService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.List; + +@Api(tags = "管理后台 - 动态表单") +@RestController +@RequestMapping("/bpm/form") +@Validated +public class BpmFormController { + + @Resource + private BpmFormService formService; + + @PostMapping("/create") + @ApiOperation("创建动态表单") + @PreAuthorize("@ss.hasPermi('bpm:form:create')") + public AjaxResult createForm(@Valid @RequestBody BpmFormCreateReqVO createReqVO) { + return AjaxResult.success(formService.createForm(createReqVO)); + } + + @PutMapping("/update") + @ApiOperation("更新动态表单") + @PreAuthorize("@ss.hasPermi('bpm:form:update')") + public AjaxResult updateForm(@Valid @RequestBody BpmFormUpdateReqVO updateReqVO) { + formService.updateForm(updateReqVO); + return AjaxResult.success(true); + } + + @DeleteMapping("/delete") + @ApiOperation("删除动态表单") + @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermi('bpm:form:delete')") + public AjaxResult deleteForm(@RequestParam("id") Long id) { + formService.deleteForm(id); + return AjaxResult.success(true); + } + + @GetMapping("/get") + @ApiOperation("获得动态表单") + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermi('bpm:form:query')") + public AjaxResult getForm(@RequestParam("id") Long id) { + BpmFormDO form = formService.getForm(id); + return AjaxResult.success(BpmFormConvert.INSTANCE.convert(form)); + } + + @GetMapping("/list-all-simple") + @ApiOperation(value = "获得动态表单的精简列表", notes = "用于表单下拉框") + public AjaxResult getSimpleForms() { + List list = formService.getFormList(); + return AjaxResult.success(BpmFormConvert.INSTANCE.convertList2(list)); + } + + @GetMapping("/page") + @ApiOperation("获得动态表单分页") + @PreAuthorize("@ss.hasPermi('bpm:form:query')") + public AjaxResult getFormPage(@Valid BpmFormPageReqVO pageVO) { + PageResult pageResult = formService.getFormPage(pageVO); + PageResult bpmFormRespVOPageResult = BpmFormConvert.INSTANCE.convertPage(pageResult); + return AjaxResult.success(bpmFormRespVOPageResult); + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/controller/definition/BpmModelController.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/controller/definition/BpmModelController.java new file mode 100644 index 00000000..4a4f1b9c --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/controller/definition/BpmModelController.java @@ -0,0 +1,93 @@ +package com.ruoyi.flowable.controller.definition; + +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.file.IoUtils; +import com.ruoyi.flowable.convert.definition.BpmModelConvert; +import com.ruoyi.flowable.domain.vo.model.*; +import com.ruoyi.flowable.service.definition.BpmModelService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.io.IOException; + +@Api(tags = "管理后台 - 流程模型") +@RestController +@RequestMapping("/bpm/model") +@Validated +public class BpmModelController { + + @Resource + private BpmModelService modelService; + + @GetMapping("/page") + @ApiOperation(value = "获得模型分页") + public AjaxResult getModelPage(BpmModelPageReqVO pageVO) { + return AjaxResult.success(modelService.getModelPage(pageVO)); + } + + @GetMapping("/get") + @ApiOperation("获得模型") + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = String.class) + @PreAuthorize("@ss.hasPermi('bpm:model:query')") + public AjaxResult getModel(@RequestParam("id") String id) { + BpmModelRespVO model = modelService.getModel(id); + return AjaxResult.success(model); + } + + @PostMapping("/create") + @ApiOperation(value = "新建模型") + @PreAuthorize("@ss.hasPermi('bpm:model:create')") + public AjaxResult createModel(@Valid @RequestBody BpmModelCreateReqVO createRetVO) { + return AjaxResult.success(modelService.createModel(createRetVO, null)); + } + + @PutMapping("/update") + @ApiOperation(value = "修改模型") + @PreAuthorize("@ss.hasPermi('bpm:model:update')") + public AjaxResult updateModel(@Valid @RequestBody BpmModelUpdateReqVO modelVO) { + modelService.updateModel(modelVO); + return AjaxResult.success(true); + } + + @PostMapping("/import") + @ApiOperation(value = "导入模型") + @PreAuthorize("@ss.hasPermi('bpm:model:import')") + public AjaxResult importModel(@Valid BpmModeImportReqVO importReqVO) throws IOException { + BpmModelCreateReqVO createReqVO = BpmModelConvert.INSTANCE.convert(importReqVO); + // 读取文件 + String bpmnXml = IoUtils.readUtf8(importReqVO.getBpmnFile().getInputStream(), false); + return AjaxResult.success(modelService.createModel(createReqVO, bpmnXml)); + } + + @PostMapping("/deploy") + @ApiOperation(value = "部署模型") + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = String.class) + @PreAuthorize("@ss.hasPermi('bpm:model:deploy')") + public AjaxResult deployModel(@RequestParam("id") String id) { + modelService.deployModel(id); + return AjaxResult.success(true); + } + + @PutMapping("/update-state") + @ApiOperation(value = "修改模型的状态", notes = "实际更新的部署的流程定义的状态") + @PreAuthorize("@ss.hasPermi('bpm:model:update')") + public AjaxResult updateModelState(@Valid @RequestBody BpmModelUpdateStateReqVO reqVO) { + modelService.updateModelState(reqVO.getId(), reqVO.getState()); + return AjaxResult.success(true); + } + + @DeleteMapping("/delete") + @ApiOperation("删除模型") + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = String.class) + @PreAuthorize("@ss.hasPermi('bpm:model:delete')") + public AjaxResult deleteModel(@RequestParam("id") String id) { + modelService.deleteModel(id); + return AjaxResult.success(true); + } +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/controller/definition/BpmProcessDefinitionController.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/controller/definition/BpmProcessDefinitionController.java new file mode 100644 index 00000000..ba053b3c --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/controller/definition/BpmProcessDefinitionController.java @@ -0,0 +1,53 @@ +package com.ruoyi.flowable.controller.definition; + +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.flowable.domain.vo.process.BpmProcessDefinitionListReqVO; +import com.ruoyi.flowable.domain.vo.process.BpmProcessDefinitionPageReqVO; +import com.ruoyi.flowable.service.definition.BpmProcessDefinitionService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + + +@Api(tags = "管理后台 - 流程定义") +@RestController +@RequestMapping("/bpm/process-definition") +@Validated +public class BpmProcessDefinitionController { + + @Resource + private BpmProcessDefinitionService bpmDefinitionService; + + @GetMapping("/page") + @ApiOperation(value = "获得流程定义分页") + @PreAuthorize("@ss.hasPermi('bpm:process-definition:query')") + public AjaxResult getProcessDefinitionPage( + BpmProcessDefinitionPageReqVO pageReqVO) { + return AjaxResult.success(bpmDefinitionService.getProcessDefinitionPage(pageReqVO)); + } + + @GetMapping("/list") + @ApiOperation(value = "获得流程定义列表") + @PreAuthorize("@ss.hasPermi('bpm:process-definition:query')") + public AjaxResult getProcessDefinitionList( + BpmProcessDefinitionListReqVO listReqVO) { + return AjaxResult.success(bpmDefinitionService.getProcessDefinitionList(listReqVO)); + } + + @GetMapping("/get-bpmn-xml") + @ApiOperation(value = "获得流程定义的 BPMN XML") + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = String.class) + @PreAuthorize("@ss.hasPermi('bpm:process-definition:query')") + public AjaxResult getProcessDefinitionBpmnXML(@RequestParam("id") String id) { + String bpmnXML = bpmDefinitionService.getProcessDefinitionBpmnXML(id); + return AjaxResult.success(bpmnXML); + } +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/controller/definition/BpmTaskAssignRuleController.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/controller/definition/BpmTaskAssignRuleController.java new file mode 100644 index 00000000..fd08eccc --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/controller/definition/BpmTaskAssignRuleController.java @@ -0,0 +1,57 @@ +package com.ruoyi.flowable.controller.definition; + +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.flowable.domain.vo.rule.BpmTaskAssignRuleCreateReqVO; +import com.ruoyi.flowable.domain.vo.rule.BpmTaskAssignRuleRespVO; +import com.ruoyi.flowable.domain.vo.rule.BpmTaskAssignRuleUpdateReqVO; +import com.ruoyi.flowable.service.definition.BpmTaskAssignRuleService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.List; + + +@Api(tags = "管理后台 - 任务分配规则") +@RestController +@RequestMapping("/bpm/task-assign-rule") +@Validated +public class BpmTaskAssignRuleController { + + @Resource + private BpmTaskAssignRuleService taskAssignRuleService; + + @GetMapping("/list") + @ApiOperation(value = "获得任务分配规则列表") + @ApiImplicitParams({ + @ApiImplicitParam(name = "modelId", value = "模型编号", example = "1024", dataTypeClass = String.class), + @ApiImplicitParam(name = "processDefinitionId", value = "流程定义的编号", example = "2048", dataTypeClass = String.class) + }) + @PreAuthorize("@ss.hasPermi('bpm:task-assign-rule:query')") + public AjaxResult getTaskAssignRuleList( + @RequestParam(value = "modelId", required = false) String modelId, + @RequestParam(value = "processDefinitionId", required = false) String processDefinitionId) { + return AjaxResult.success(taskAssignRuleService.getTaskAssignRuleList(modelId, processDefinitionId)); + } + + @PostMapping("/create") + @ApiOperation(value = "创建任务分配规则") + @PreAuthorize("@ss.hasPermi('bpm:task-assign-rule:create')") + public AjaxResult createTaskAssignRule(@Valid @RequestBody BpmTaskAssignRuleCreateReqVO reqVO) { + return AjaxResult.success(taskAssignRuleService.createTaskAssignRule(reqVO)); + } + + @PutMapping("/update") + @ApiOperation(value = "更新任务分配规则") + @PreAuthorize("@ss.hasPermi('bpm:task-assign-rule:update')") + public AjaxResult updateTaskAssignRule(@Valid @RequestBody BpmTaskAssignRuleUpdateReqVO reqVO) { + taskAssignRuleService.updateTaskAssignRule(reqVO); + return AjaxResult.success(true); + } +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/controller/definition/BpmUserGroupController.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/controller/definition/BpmUserGroupController.java new file mode 100644 index 00000000..cd4ae3b1 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/controller/definition/BpmUserGroupController.java @@ -0,0 +1,83 @@ +package com.ruoyi.flowable.controller.definition; + +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.CommonStatusEnum; +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.flowable.convert.definition.BpmUserGroupConvert; +import com.ruoyi.flowable.domain.entity.definition.BpmUserGroupDO; +import com.ruoyi.flowable.domain.vo.group.BpmUserGroupCreateReqVO; +import com.ruoyi.flowable.domain.vo.group.BpmUserGroupPageReqVO; +import com.ruoyi.flowable.domain.vo.group.BpmUserGroupUpdateReqVO; +import com.ruoyi.flowable.service.definition.BpmUserGroupService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.List; + + +@Api(tags = "管理后台 - 用户组") +@RestController +@RequestMapping("/bpm/user-group") +@Validated +public class BpmUserGroupController { + + @Resource + private BpmUserGroupService userGroupService; + + @PostMapping("/create") + @ApiOperation("创建用户组") + @PreAuthorize("@ss.hasPermi('bpm:user-group:create')") + public AjaxResult createUserGroup(@Valid @RequestBody BpmUserGroupCreateReqVO createReqVO) { + return AjaxResult.success(userGroupService.createUserGroup(createReqVO)); + } + + @PutMapping("/update") + @ApiOperation("更新用户组") + @PreAuthorize("@ss.hasPermi('bpm:user-group:update')") + public AjaxResult updateUserGroup(@Valid @RequestBody BpmUserGroupUpdateReqVO updateReqVO) { + userGroupService.updateUserGroup(updateReqVO); + return AjaxResult.success(true); + } + + @DeleteMapping("/delete") + @ApiOperation("删除用户组") + @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermi('bpm:user-group:delete')") + public AjaxResult deleteUserGroup(@RequestParam("id") Long id) { + userGroupService.deleteUserGroup(id); + return AjaxResult.success(true); + } + + @GetMapping("/get") + @ApiOperation("获得用户组") + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermi('bpm:user-group:query')") + public AjaxResult getUserGroup(@RequestParam("id") Long id) { + BpmUserGroupDO userGroup = userGroupService.getUserGroup(id); + return AjaxResult.success(BpmUserGroupConvert.INSTANCE.convert(userGroup)); + } + + @GetMapping("/page") + @ApiOperation("获得用户组分页") + @PreAuthorize("@ss.hasPermi('bpm:user-group:query')") + public AjaxResult getUserGroupPage(@Valid BpmUserGroupPageReqVO pageVO) { + PageResult pageResult = userGroupService.getUserGroupPage(pageVO); + return AjaxResult.success(BpmUserGroupConvert.INSTANCE.convertPage(pageResult)); + } + + @GetMapping("/list-all-simple") + @ApiOperation(value = "获取用户组精简信息列表", notes = "只包含被开启的用户组,主要用于前端的下拉选项") + public AjaxResult getSimpleUserGroups() { + // 获用户门列表,只要开启状态的 + List list = userGroupService.getUserGroupListByStatus(CommonStatusEnum.ENABLE.getStatus()); + // 排序后,返回给前端 + return AjaxResult.success(BpmUserGroupConvert.INSTANCE.convertList2(list)); + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/controller/oa/BpmOALeaveController.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/controller/oa/BpmOALeaveController.java new file mode 100644 index 00000000..fd3c4943 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/controller/oa/BpmOALeaveController.java @@ -0,0 +1,62 @@ +package com.ruoyi.flowable.controller.oa; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.metadata.OrderItem; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.flowable.convert.oa.BpmOALeaveConvert; +import com.ruoyi.flowable.domain.entity.oa.BpmOALeaveDO; +import com.ruoyi.flowable.domain.vo.oa.BpmOALeaveCreateReqVO; +import com.ruoyi.flowable.service.oa.BpmOALeaveService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; + +/** + * OA 请假申请 Controller,用于演示自己存储数据,接入工作流的例子 + * + * @author jason + * hasPermi + */ +@Api(tags = "管理后台 - OA 请假申请") +@RestController +@RequestMapping("/bpm/oa/leave") +@Validated +public class BpmOALeaveController extends BaseController { + + @Resource + private BpmOALeaveService leaveService; + + @PostMapping("/create") + @PreAuthorize("@ss.hasPermi('bpm:oa-leave:create')") + @ApiOperation("创建请求申请") + public AjaxResult createLeave(@Valid @RequestBody BpmOALeaveCreateReqVO createReqVO) { + return AjaxResult.success(leaveService.createLeave(SecurityUtils.getUserId(), createReqVO)); + } + + @GetMapping("/get") + @PreAuthorize("@ss.hasPermi('bpm:oa-leave:query')") + @ApiOperation("获得请假申请") + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) + public AjaxResult getLeave(@RequestParam("id") Long id) { + BpmOALeaveDO leave = leaveService.getLeave(id); + return AjaxResult.success(BpmOALeaveConvert.INSTANCE.convert(leave)); + } + + @GetMapping("/page") + @PreAuthorize("@ss.hasPermi('bpm:oa-leave:query')") + @ApiOperation("获得请假申请分页") + public TableDataInfo list(BpmOALeaveDO bpmOaLeave) { + IPage list = leaveService.selectList(getPage(OrderItem.asc("result"),OrderItem.desc("id")), bpmOaLeave); + return getDataTable(list); + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/controller/task/BpmActivityController.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/controller/task/BpmActivityController.java new file mode 100644 index 00000000..2d1bc487 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/controller/task/BpmActivityController.java @@ -0,0 +1,35 @@ +package com.ruoyi.flowable.controller.task; + +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.flowable.service.task.BpmActivityService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + + +@Api(tags = "管理后台 - 流程活动实例") +@RestController +@RequestMapping("/bpm/activity") +@Validated +public class BpmActivityController { + + @Resource + private BpmActivityService activityService; + + @GetMapping("/list") + @ApiOperation(value = "生成指定流程实例的高亮流程图", + notes = "只高亮进行中的任务。不过要注意,该接口暂时没用,通过前端的 ProcessViewer.vue 界面的 highlightDiagram 方法生成") + @ApiImplicitParam(name = "processInstanceId", value = "流程实例的编号", required = true, dataTypeClass = String.class) + @PreAuthorize("@ss.hasPermi('bpm:task:query')") + public AjaxResult getActivityList(@RequestParam("processInstanceId") String processInstanceId) { + return AjaxResult.success(activityService.getActivityListByProcessInstanceId(processInstanceId)); + } +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/controller/task/BpmProcessInstanceController.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/controller/task/BpmProcessInstanceController.java new file mode 100644 index 00000000..e84702ba --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/controller/task/BpmProcessInstanceController.java @@ -0,0 +1,57 @@ +package com.ruoyi.flowable.controller.task; + +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.flowable.domain.vo.instance.*; +import com.ruoyi.flowable.service.task.BpmProcessInstanceService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; + + +@Api(tags = "管理后台 - 流程实例") // 流程实例,通过流程定义创建的一次“申请” +@RestController +@RequestMapping("/bpm/process-instance") +@Validated +public class BpmProcessInstanceController { + + @Resource + private BpmProcessInstanceService processInstanceService; + + @GetMapping("/my-page") + @ApiOperation(value = "获得我的实例分页列表", notes = "在【我的流程】菜单中,进行调用") + @PreAuthorize("@ss.hasPermi('bpm:process-instance:query')") + public AjaxResult getMyProcessInstancePage( + @Valid BpmProcessInstanceMyPageReqVO pageReqVO) { + return AjaxResult.success(processInstanceService.getMyProcessInstancePage(SecurityUtils.getUserId(), pageReqVO)); + } + + @PostMapping("/create") + @ApiOperation("新建流程实例") + @PreAuthorize("@ss.hasPermi('bpm:process-instance:query')") + public AjaxResult createProcessInstance(@Valid @RequestBody BpmProcessInstanceCreateReqVO createReqVO) { + return AjaxResult.success(processInstanceService.createProcessInstance(SecurityUtils.getUserId(), createReqVO)); + } + + @GetMapping("/get") + @ApiOperation(value = "获得指定流程实例", notes = "在【流程详细】界面中,进行调用") + @ApiImplicitParam(name = "id", value = "流程实例的编号", required = true, dataTypeClass = String.class) + @PreAuthorize("@ss.hasPermi('bpm:process-instance:query')") + public AjaxResult getProcessInstance(@RequestParam("id") String id) { + return AjaxResult.success(processInstanceService.getProcessInstanceVO(id)); + } + + @DeleteMapping("/cancel") + @ApiOperation(value = "取消流程实例", notes = "撤回发起的流程") + @PreAuthorize("@ss.hasPermi('bpm:process-instance:cancel')") + public AjaxResult cancelProcessInstance(@Valid @RequestBody BpmProcessInstanceCancelReqVO cancelReqVO) { + processInstanceService.cancelProcessInstance(SecurityUtils.getUserId(), cancelReqVO); + return AjaxResult.success(); + } +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/controller/task/BpmTaskController.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/controller/task/BpmTaskController.java new file mode 100644 index 00000000..9d2cd26f --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/controller/task/BpmTaskController.java @@ -0,0 +1,81 @@ +package com.ruoyi.flowable.controller.task; + +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.flowable.domain.vo.task.*; +import com.ruoyi.flowable.service.task.BpmTaskService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; + + +@Api(tags = "管理后台 - 流程任务实例") +@RestController +@RequestMapping("/bpm/task") +@Validated +public class BpmTaskController { + + @Resource + private BpmTaskService taskService; + + @GetMapping("todo-page") + @ApiOperation("获取 Todo 待办任务分页") + @PreAuthorize("@ss.hasPermi('bpm:task:query')") + public AjaxResult getTodoTaskPage(@Valid BpmTaskTodoPageReqVO pageVO) { + return AjaxResult.success(taskService.getTodoTaskPage(SecurityUtils.getUserId(), pageVO)); + } + + @GetMapping("done-page") + @ApiOperation("获取 Done 已办任务分页") + @PreAuthorize("@ss.hasPermi('bpm:task:query')") + public AjaxResult getDoneTaskPage(@Valid BpmTaskDonePageReqVO pageVO) { + return AjaxResult.success(taskService.getDoneTaskPage(SecurityUtils.getUserId(), pageVO)); + } + + @GetMapping("/list-by-process-instance-id") + @ApiOperation(value = "获得指定流程实例的任务列表", notes = "包括完成的、未完成的") + @ApiImplicitParam(name = "processInstanceId", value = "流程实例的编号", required = true, dataTypeClass = String.class) + @PreAuthorize("@ss.hasPermi('bpm:task:query')") + public AjaxResult getTaskListByProcessInstanceId( + @RequestParam("processInstanceId") String processInstanceId) { + return AjaxResult.success(taskService.getTaskListByProcessInstanceId(processInstanceId)); + } + + @PutMapping("/approve") + @ApiOperation("通过任务") + @PreAuthorize("@ss.hasPermi('bpm:task:update')") + public AjaxResult approveTask(@Valid @RequestBody BpmTaskApproveReqVO reqVO) { + taskService.approveTask(SecurityUtils.getUserId(), reqVO); + return AjaxResult.success(true); + } + + @PutMapping("/reject") + @ApiOperation("不通过任务") + @PreAuthorize("@ss.hasPermi('bpm:task:update')") + public AjaxResult rejectTask(@Valid @RequestBody BpmTaskRejectReqVO reqVO) { + taskService.rejectTask(SecurityUtils.getUserId(), reqVO); + return AjaxResult.success(true); + } + + @PutMapping("/update-assignee") + @ApiOperation(value = "更新任务的负责人", notes = "用于【流程详情】的【转派】按钮") + @PreAuthorize("@ss.hasPermi('bpm:task:update')") + public AjaxResult updateTaskAssignee(@Valid @RequestBody BpmTaskUpdateAssigneeReqVO reqVO) { + taskService.updateTaskAssignee(SecurityUtils.getUserId(), reqVO); + return AjaxResult.success(true); + } + + @ApiOperation(value = "获取可驳回节点列表", notes = "获取可驳回节点列表") + @GetMapping(value = "/getBackNodesByProcessInstanceId/{processInstanceId}/{taskId}") + public AjaxResult getBackNodesByProcessInstanceId(@PathVariable String processInstanceId,@PathVariable String taskId) { + return AjaxResult.success(taskService.getBackNodesByProcessInstanceId(processInstanceId,taskId)); + } + + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/definition/BpmFormConvert.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/definition/BpmFormConvert.java new file mode 100644 index 00000000..762f6e15 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/definition/BpmFormConvert.java @@ -0,0 +1,34 @@ +package com.ruoyi.flowable.convert.definition; + +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.flowable.domain.entity.definition.BpmFormDO; +import com.ruoyi.flowable.domain.vo.form.BpmFormCreateReqVO; +import com.ruoyi.flowable.domain.vo.form.BpmFormRespVO; +import com.ruoyi.flowable.domain.vo.form.BpmFormSimpleRespVO; +import com.ruoyi.flowable.domain.vo.form.BpmFormUpdateReqVO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +/** + * 动态表单 Convert + * + * @author 芋艿 + */ +@Mapper +public interface BpmFormConvert { + + BpmFormConvert INSTANCE = Mappers.getMapper(BpmFormConvert.class); + + BpmFormDO convert(BpmFormCreateReqVO bean); + + BpmFormDO convert(BpmFormUpdateReqVO bean); + + BpmFormRespVO convert(BpmFormDO bean); + + List convertList2(List list); + + PageResult convertPage(PageResult page); + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/definition/BpmModelConvert.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/definition/BpmModelConvert.java new file mode 100644 index 00000000..4ebc9bf9 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/definition/BpmModelConvert.java @@ -0,0 +1,140 @@ +package com.ruoyi.flowable.convert.definition; + +import cn.hutool.core.util.StrUtil; +import com.ruoyi.common.utils.JsonUtils; +import com.ruoyi.common.utils.collection.CollectionUtils; +import com.ruoyi.flowable.domain.dto.definition.BpmModelMetaInfoRespDTO; +import com.ruoyi.flowable.domain.dto.definition.BpmProcessDefinitionCreateReqDTO; +import com.ruoyi.flowable.domain.entity.definition.BpmFormDO; +import com.ruoyi.flowable.domain.vo.model.*; +import org.flowable.common.engine.impl.db.SuspensionState; +import org.flowable.engine.repository.Deployment; +import org.flowable.engine.repository.Model; +import org.flowable.engine.repository.ProcessDefinition; +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; +import org.mapstruct.factory.Mappers; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * 流程模型 Convert + * + * @author yunlongn + */ +@Mapper +public interface BpmModelConvert { + + BpmModelConvert INSTANCE = Mappers.getMapper(BpmModelConvert.class); + + default List convertList(List list, Map formMap, + Map deploymentMap, + Map processDefinitionMap) { + return CollectionUtils.convertList(list, model -> { + BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class); + BpmFormDO form = metaInfo != null ? formMap.get(metaInfo.getFormId()) : null; + Deployment deployment = model.getDeploymentId() != null ? deploymentMap.get(model.getDeploymentId()) : null; + ProcessDefinition processDefinition = model.getDeploymentId() != null ? processDefinitionMap.get(model.getDeploymentId()) : null; + return convert(model, form, deployment, processDefinition); + }); + } + + default BpmModelPageItemRespVO convert(Model model, BpmFormDO form, Deployment deployment, ProcessDefinition processDefinition) { + BpmModelPageItemRespVO modelRespVO = new BpmModelPageItemRespVO(); + modelRespVO.setId(model.getId()); + modelRespVO.setCreateTime(model.getCreateTime()); + // 通用 copy + copyTo(model, modelRespVO); + // Form + if (form != null) { + modelRespVO.setFormId(form.getId()); + modelRespVO.setFormName(form.getName()); + } + // ProcessDefinition + modelRespVO.setProcessDefinition(this.convert(processDefinition)); + if (modelRespVO.getProcessDefinition() != null) { + modelRespVO.getProcessDefinition().setSuspensionState(processDefinition.isSuspended() ? + SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode()); + modelRespVO.getProcessDefinition().setDeploymentTime(deployment.getDeploymentTime()); + } + return modelRespVO; + } + + default BpmModelRespVO convert(Model model) { + BpmModelRespVO modelRespVO = new BpmModelRespVO(); + modelRespVO.setId(model.getId()); + modelRespVO.setCreateTime(model.getCreateTime()); + // 通用 copy + copyTo(model, modelRespVO); + return modelRespVO; + } + + default void copyTo(Model model, BpmModelBaseVO to) { + to.setName(model.getName()); + to.setKey(model.getKey()); + to.setCategory(model.getCategory()); + // metaInfo + BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class); + copyTo(metaInfo, to); + } + + BpmModelCreateReqVO convert(BpmModeImportReqVO bean); + + default BpmProcessDefinitionCreateReqDTO convert2(Model model, BpmFormDO form) { + BpmProcessDefinitionCreateReqDTO createReqDTO = new BpmProcessDefinitionCreateReqDTO(); + createReqDTO.setModelId(model.getId()); + createReqDTO.setName(model.getName()); + createReqDTO.setKey(model.getKey()); + createReqDTO.setCategory(model.getCategory()); + BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class); + // metaInfo + copyTo(metaInfo, createReqDTO); + // form + if (form != null) { + createReqDTO.setFormConf(form.getConf()); + createReqDTO.setFormFields(form.getFields()); + } + return createReqDTO; + } + + void copyTo(BpmModelMetaInfoRespDTO from, @MappingTarget BpmProcessDefinitionCreateReqDTO to); + + void copyTo(BpmModelMetaInfoRespDTO from, @MappingTarget BpmModelBaseVO to); + + BpmModelPageItemRespVO.ProcessDefinition convert(ProcessDefinition bean); + + default void copy(Model model, BpmModelCreateReqVO bean) { + model.setName(bean.getName()); + model.setKey(bean.getKey()); + model.setMetaInfo(buildMetaInfoStr(null, bean.getDescription(), null, null, + null, null)); + } + + default void copy(Model model, BpmModelUpdateReqVO bean) { + model.setName(bean.getName()); + model.setCategory(bean.getCategory()); + model.setMetaInfo(buildMetaInfoStr(JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class), + bean.getDescription(), bean.getFormType(), bean.getFormId(), + bean.getFormCustomCreatePath(), bean.getFormCustomViewPath())); + } + + default String buildMetaInfoStr(BpmModelMetaInfoRespDTO metaInfo, String description, Integer formType, + Long formId, String formCustomCreatePath, String formCustomViewPath) { + if (metaInfo == null) { + metaInfo = new BpmModelMetaInfoRespDTO(); + } + // 只有非空,才进行设置,避免更新时的覆盖 + if (StrUtil.isNotEmpty(description)) { + metaInfo.setDescription(description); + } + if (Objects.nonNull(formType)) { + metaInfo.setFormType(formType); + metaInfo.setFormId(formId); + metaInfo.setFormCustomCreatePath(formCustomCreatePath); + metaInfo.setFormCustomViewPath(formCustomViewPath); + } + return JsonUtils.toJsonString(metaInfo); + } +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/definition/BpmProcessDefinitionConvert.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/definition/BpmProcessDefinitionConvert.java new file mode 100644 index 00000000..927c6cb0 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/definition/BpmProcessDefinitionConvert.java @@ -0,0 +1,82 @@ +package com.ruoyi.flowable.convert.definition; + +import com.ruoyi.common.utils.collection.CollectionUtils; +import com.ruoyi.flowable.domain.dto.definition.BpmProcessDefinitionCreateReqDTO; +import com.ruoyi.flowable.domain.entity.definition.BpmFormDO; +import com.ruoyi.flowable.domain.entity.definition.BpmProcessDefinitionExtDO; +import com.ruoyi.flowable.domain.vo.process.BpmProcessDefinitionPageItemRespVO; +import com.ruoyi.flowable.domain.vo.process.BpmProcessDefinitionRespVO; +import org.flowable.common.engine.impl.db.SuspensionState; +import org.flowable.engine.repository.Deployment; +import org.flowable.engine.repository.ProcessDefinition; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; +import org.mapstruct.Named; +import org.mapstruct.factory.Mappers; + +import java.util.List; +import java.util.Map; + +/** + * Bpm 流程定义的 Convert + * + * @author yunlong.li + */ +@Mapper +public interface BpmProcessDefinitionConvert { + + BpmProcessDefinitionConvert INSTANCE = Mappers.getMapper(BpmProcessDefinitionConvert.class); + + BpmProcessDefinitionPageItemRespVO convert(ProcessDefinition bean); + + BpmProcessDefinitionExtDO convert2(BpmProcessDefinitionCreateReqDTO bean); + + default List convertList(List list, Map deploymentMap, + Map processDefinitionDOMap, Map formMap) { + return CollectionUtils.convertList(list, definition -> { + Deployment deployment = definition.getDeploymentId() != null ? deploymentMap.get(definition.getDeploymentId()) : null; + BpmProcessDefinitionExtDO definitionDO = processDefinitionDOMap.get(definition.getId()); + BpmFormDO form = definitionDO != null ? formMap.get(definitionDO.getFormId()) : null; + return convert(definition, deployment, definitionDO, form); + }); + } + + default List convertList3(List list, + Map processDefinitionDOMap) { + return CollectionUtils.convertList(list, processDefinition -> { + BpmProcessDefinitionRespVO respVO = convert3(processDefinition); + BpmProcessDefinitionExtDO processDefinitionExtDO = processDefinitionDOMap.get(processDefinition.getId()); + // 复制通用属性 + copyTo(processDefinitionExtDO, respVO); + return respVO; + }); + } + + @Mapping(source = "suspended", target = "suspensionState", qualifiedByName = "convertSuspendedToSuspensionState") + BpmProcessDefinitionRespVO convert3(ProcessDefinition bean); + + @Named("convertSuspendedToSuspensionState") + default Integer convertSuspendedToSuspensionState(boolean suspended) { + return suspended ? SuspensionState.SUSPENDED.getStateCode() : + SuspensionState.ACTIVE.getStateCode(); + } + + default BpmProcessDefinitionPageItemRespVO convert(ProcessDefinition bean, Deployment deployment, + BpmProcessDefinitionExtDO processDefinitionExtDO, BpmFormDO form) { + BpmProcessDefinitionPageItemRespVO respVO = convert(bean); + respVO.setSuspensionState(bean.isSuspended() ? SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode()); + if (deployment != null) { + respVO.setDeploymentTime(deployment.getDeploymentTime()); + } + if (form != null) { + respVO.setFormName(form.getName()); + } + // 复制通用属性 + copyTo(processDefinitionExtDO, respVO); + return respVO; + } + + @Mapping(source = "from.id", target = "to.id", ignore = true) + void copyTo(BpmProcessDefinitionExtDO from, @MappingTarget BpmProcessDefinitionRespVO to); +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/definition/BpmTaskAssignRuleConvert.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/definition/BpmTaskAssignRuleConvert.java new file mode 100644 index 00000000..b5d4b4c9 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/definition/BpmTaskAssignRuleConvert.java @@ -0,0 +1,40 @@ +package com.ruoyi.flowable.convert.definition; + +import com.ruoyi.common.utils.collection.CollectionUtils; +import com.ruoyi.flowable.domain.entity.definition.BpmTaskAssignRuleDO; +import com.ruoyi.flowable.domain.vo.rule.BpmTaskAssignRuleCreateReqVO; +import com.ruoyi.flowable.domain.vo.rule.BpmTaskAssignRuleRespVO; +import com.ruoyi.flowable.domain.vo.rule.BpmTaskAssignRuleUpdateReqVO; +import org.flowable.bpmn.model.UserTask; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; +import java.util.Map; + +@Mapper +public interface BpmTaskAssignRuleConvert { + BpmTaskAssignRuleConvert INSTANCE = Mappers.getMapper(BpmTaskAssignRuleConvert.class); + + default List convertList(List tasks, List rules) { + Map ruleMap = CollectionUtils.convertMap(rules, BpmTaskAssignRuleDO::getTaskDefinitionKey); + // 以 UserTask 为主维度,原因是:流程图编辑后,一些规则实际就没用了。 + return CollectionUtils.convertList(tasks, task -> { + BpmTaskAssignRuleRespVO respVO = convert(ruleMap.get(task.getId())); + if (respVO == null) { + respVO = new BpmTaskAssignRuleRespVO(); + respVO.setTaskDefinitionKey(task.getId()); + } + respVO.setTaskDefinitionName(task.getName()); + return respVO; + }); + } + + BpmTaskAssignRuleRespVO convert(BpmTaskAssignRuleDO bean); + + BpmTaskAssignRuleDO convert(BpmTaskAssignRuleCreateReqVO bean); + + BpmTaskAssignRuleDO convert(BpmTaskAssignRuleUpdateReqVO bean); + + List convertList2(List list); +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/definition/BpmUserGroupConvert.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/definition/BpmUserGroupConvert.java new file mode 100644 index 00000000..a9cc96ba --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/definition/BpmUserGroupConvert.java @@ -0,0 +1,37 @@ +package com.ruoyi.flowable.convert.definition; + +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.flowable.domain.entity.definition.BpmUserGroupDO; +import com.ruoyi.flowable.domain.vo.group.BpmUserGroupCreateReqVO; +import com.ruoyi.flowable.domain.vo.group.BpmUserGroupRespVO; +import com.ruoyi.flowable.domain.vo.group.BpmUserGroupUpdateReqVO; +import org.mapstruct.Mapper; +import org.mapstruct.Named; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +/** + * 用户组 Convert + * + * hasPermi + */ +@Mapper +public interface BpmUserGroupConvert { + + BpmUserGroupConvert INSTANCE = Mappers.getMapper(BpmUserGroupConvert.class); + + BpmUserGroupDO convert(BpmUserGroupCreateReqVO bean); + + BpmUserGroupDO convert(BpmUserGroupUpdateReqVO bean); + + BpmUserGroupRespVO convert(BpmUserGroupDO bean); + + List convertList(List list); + + PageResult convertPage(PageResult page); + + @Named("convertList2") + List convertList2(List list); + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/message/BpmMessageConvert.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/message/BpmMessageConvert.java new file mode 100644 index 00000000..c3267eb3 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/message/BpmMessageConvert.java @@ -0,0 +1,16 @@ +package com.ruoyi.flowable.convert.message; + +import com.ruoyi.flowable.domain.dto.send.SmsSendSingleToUserReqDTO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.Map; + +@Mapper +public interface BpmMessageConvert { + + BpmMessageConvert INSTANCE = Mappers.getMapper(BpmMessageConvert.class); + + SmsSendSingleToUserReqDTO convert(Long userId, String templateCode, Map templateParams); + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/oa/BpmOALeaveConvert.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/oa/BpmOALeaveConvert.java new file mode 100644 index 00000000..ac8ee5a5 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/oa/BpmOALeaveConvert.java @@ -0,0 +1,30 @@ +package com.ruoyi.flowable.convert.oa; + +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.flowable.domain.entity.oa.BpmOALeaveDO; +import com.ruoyi.flowable.domain.vo.oa.BpmOALeaveCreateReqVO; +import com.ruoyi.flowable.domain.vo.oa.BpmOALeaveRespVO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +/** + * 请假申请 Convert + * + * @author 芋艿 + */ +@Mapper +public interface BpmOALeaveConvert { + + BpmOALeaveConvert INSTANCE = Mappers.getMapper(BpmOALeaveConvert.class); + + BpmOALeaveDO convert(BpmOALeaveCreateReqVO bean); + + BpmOALeaveRespVO convert(BpmOALeaveDO bean); + + List convertList(List list); + + PageResult convertPage(PageResult page); + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/task/BpmActivityConvert.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/task/BpmActivityConvert.java new file mode 100644 index 00000000..285c5511 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/task/BpmActivityConvert.java @@ -0,0 +1,29 @@ +package com.ruoyi.flowable.convert.task; + +import com.ruoyi.flowable.domain.vo.activity.BpmActivityRespVO; +import org.flowable.engine.history.HistoricActivityInstance; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +/** + * BPM 活动 Convert + * + * hasPermi + */ +@Mapper +public interface BpmActivityConvert { + + BpmActivityConvert INSTANCE = Mappers.getMapper(BpmActivityConvert.class); + + List convertList(List list); + + @Mappings({ + @Mapping(source = "activityId", target = "key"), + @Mapping(source = "activityType", target = "type") + }) + BpmActivityRespVO convert(HistoricActivityInstance bean); +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/task/BpmProcessInstanceConvert.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/task/BpmProcessInstanceConvert.java new file mode 100644 index 00000000..0b0a7391 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/task/BpmProcessInstanceConvert.java @@ -0,0 +1,114 @@ +package com.ruoyi.flowable.convert.task; + +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.common.utils.NumberUtils; +import com.ruoyi.flowable.domain.dto.message.BpmMessageSendWhenProcessInstanceApproveReqDTO; +import com.ruoyi.flowable.domain.dto.message.BpmMessageSendWhenProcessInstanceRejectReqDTO; +import com.ruoyi.flowable.domain.dto.user.AdminUserRespDTO; +import com.ruoyi.flowable.domain.dto.user.DeptRespDTO; +import com.ruoyi.flowable.domain.entity.definition.BpmProcessDefinitionExtDO; +import com.ruoyi.flowable.domain.entity.task.BpmProcessInstanceExtDO; +import com.ruoyi.flowable.domain.vo.instance.BpmProcessInstancePageItemRespVO; +import com.ruoyi.flowable.domain.vo.instance.BpmProcessInstanceRespVO; +import com.ruoyi.flowable.framework.bpm.core.event.BpmProcessInstanceResultEvent; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.task.api.Task; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; +import org.mapstruct.factory.Mappers; + +import java.util.List; +import java.util.Map; + +/** + * 流程实例 Convert + * + * hasPermi + */ +@Mapper +public interface BpmProcessInstanceConvert { + + BpmProcessInstanceConvert INSTANCE = Mappers.getMapper(BpmProcessInstanceConvert.class); + + default PageResult convertPage(PageResult page, + Map> taskMap) { + List list = convertList(page.getList()); + list.forEach(respVO -> respVO.setTasks(convertList2(taskMap.get(respVO.getId())))); + return new PageResult<>(list, page.getTotal()); + } + + List convertList(List list); + + @Mapping(source = "processInstanceId", target = "id") + BpmProcessInstancePageItemRespVO convert(BpmProcessInstanceExtDO bean); + + List convertList2(List tasks); + + default BpmProcessInstanceRespVO convert2(HistoricProcessInstance processInstance, BpmProcessInstanceExtDO processInstanceExt, + ProcessDefinition processDefinition, BpmProcessDefinitionExtDO processDefinitionExt, + String bpmnXml, AdminUserRespDTO startUser, DeptRespDTO dept) { + BpmProcessInstanceRespVO respVO = convert2(processInstance); + copyTo(processInstanceExt, respVO); + // definition + respVO.setProcessDefinition(convert2(processDefinition)); + copyTo(processDefinitionExt, respVO.getProcessDefinition()); + respVO.getProcessDefinition().setBpmnXml(bpmnXml); + // user + if (startUser != null) { + respVO.setStartUser(convert2(startUser)); + if (dept != null) { + respVO.getStartUser().setDeptName(dept.getName()); + } + } + return respVO; + } + + BpmProcessInstanceRespVO convert2(HistoricProcessInstance bean); + + @Mapping(source = "from.id", target = "to.id", ignore = true) + void copyTo(BpmProcessInstanceExtDO from, @MappingTarget BpmProcessInstanceRespVO to); + + BpmProcessInstanceRespVO.ProcessDefinition convert2(ProcessDefinition bean); + + @Mapping(source = "from.id", target = "to.id", ignore = true) + void copyTo(BpmProcessDefinitionExtDO from, @MappingTarget BpmProcessInstanceRespVO.ProcessDefinition to); + + BpmProcessInstanceRespVO.User convert2(AdminUserRespDTO bean); + + default BpmProcessInstanceResultEvent convert(Object source, HistoricProcessInstance instance, Integer result) { + BpmProcessInstanceResultEvent event = new BpmProcessInstanceResultEvent(source); + event.setId(instance.getId()); + event.setProcessDefinitionKey(instance.getProcessDefinitionKey()); + event.setBusinessKey(instance.getBusinessKey()); + event.setResult(result); + return event; + } + + default BpmProcessInstanceResultEvent convert(Object source, ProcessInstance instance, Integer result) { + BpmProcessInstanceResultEvent event = new BpmProcessInstanceResultEvent(source); + event.setId(instance.getId()); + event.setProcessDefinitionKey(instance.getProcessDefinitionKey()); + event.setBusinessKey(instance.getBusinessKey()); + event.setResult(result); + return event; + } + + default BpmMessageSendWhenProcessInstanceApproveReqDTO convert2ApprovedReq(ProcessInstance instance){ + return new BpmMessageSendWhenProcessInstanceApproveReqDTO() + .setStartUserId(NumberUtils.parseLong(instance.getStartUserId())) + .setProcessInstanceId(instance.getId()) + .setProcessInstanceName(instance.getName()); + } + + default BpmMessageSendWhenProcessInstanceRejectReqDTO convert2RejectReq(ProcessInstance instance, String reason) { + return new BpmMessageSendWhenProcessInstanceRejectReqDTO() + .setProcessInstanceName(instance.getName()) + .setProcessInstanceId(instance.getId()) + .setReason(reason) + .setStartUserId(NumberUtils.parseLong(instance.getStartUserId())); + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/task/BpmTaskConvert.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/task/BpmTaskConvert.java new file mode 100644 index 00000000..719ab2ba --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/task/BpmTaskConvert.java @@ -0,0 +1,170 @@ +package com.ruoyi.flowable.convert.task; + +import com.ruoyi.common.utils.NumberUtils; +import com.ruoyi.common.utils.collection.CollectionUtils; +import com.ruoyi.flowable.domain.dto.message.BpmMessageSendWhenTaskCreatedReqDTO; +import com.ruoyi.flowable.domain.dto.user.AdminUserRespDTO; +import com.ruoyi.flowable.domain.dto.user.DeptRespDTO; +import com.ruoyi.flowable.domain.entity.task.BpmTaskExtDO; +import com.ruoyi.flowable.domain.vo.task.BpmTaskDonePageItemRespVO; +import com.ruoyi.flowable.domain.vo.task.BpmTaskRespVO; +import com.ruoyi.flowable.domain.vo.task.BpmTaskTodoPageItemRespVO; +import org.flowable.common.engine.impl.db.SuspensionState; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.task.api.Task; +import org.flowable.task.api.history.HistoricTaskInstance; +import org.mapstruct.*; +import org.mapstruct.factory.Mappers; +import org.springframework.beans.BeanUtils; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Bpm 任务 Convert + * + * hasPermi + */ +@Mapper +public interface BpmTaskConvert { + + BpmTaskConvert INSTANCE = Mappers.getMapper(BpmTaskConvert.class); + + /** + * 复制对象 + * + * @param source 源 要复制的对象 + * @param target 目标 复制到此对象 + * @param + * + * @return + */ + public static T copy(Object source, Class target) { + if (source == null || target == null) { + return null; + } + try { + T newInstance = target.newInstance(); + BeanUtils.copyProperties(source, newInstance); + return newInstance; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + default List copyList(List source, Class target) { + if (null == source || source.isEmpty()) { + return Collections.emptyList(); + } + return source.stream().map(e -> copy(e, target)).collect(Collectors.toList()); + } + + default List convertList1(List tasks, + Map processInstanceMap, Map userMap) { + return CollectionUtils.convertList(tasks, task -> { + BpmTaskTodoPageItemRespVO respVO = convert1(task); + ProcessInstance processInstance = processInstanceMap.get(task.getProcessInstanceId()); + if (processInstance != null) { + AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId())); + respVO.setProcessInstance(convert(processInstance, startUser)); + } + return respVO; + }); + } + + @Mapping(source = "suspended", target = "suspensionState", qualifiedByName = "convertSuspendedToSuspensionState") + BpmTaskTodoPageItemRespVO convert1(Task bean); + + @Named("convertSuspendedToSuspensionState") + default Integer convertSuspendedToSuspensionState(boolean suspended) { + return suspended ? SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode(); + } + + default List convertList2(List tasks, + Map bpmTaskExtDOMap, Map historicProcessInstanceMap, + Map userMap) { + return CollectionUtils.convertList(tasks, task -> { + BpmTaskDonePageItemRespVO respVO = convert2(task); + BpmTaskExtDO taskExtDO = bpmTaskExtDOMap.get(task.getId()); + copyTo(taskExtDO, respVO); + HistoricProcessInstance processInstance = historicProcessInstanceMap.get(task.getProcessInstanceId()); + if (processInstance != null) { + AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId())); + respVO.setProcessInstance(convert(processInstance, startUser)); + } + return respVO; + }); + } + + BpmTaskDonePageItemRespVO convert2(HistoricTaskInstance bean); + + @Mappings({@Mapping(source = "processInstance.id", target = "id"), + @Mapping(source = "processInstance.name", target = "name"), + @Mapping(source = "processInstance.startUserId", target = "startUserId"), + @Mapping(source = "processInstance.processDefinitionId", target = "processDefinitionId"), + @Mapping(source = "processInstance.businessKey", target = "businessKey"), + @Mapping(source = "processInstance.processDefinitionKey", target = "processDefinitionKey"), + @Mapping(source = "startUser.nickname", target = "startUserNickname")}) + BpmTaskTodoPageItemRespVO.ProcessInstance convert(ProcessInstance processInstance, AdminUserRespDTO startUser); + + default List convertList3(List tasks, + Map bpmTaskExtDOMap, HistoricProcessInstance processInstance, + Map userMap, Map deptMap) { + return CollectionUtils.convertList(tasks, task -> { + BpmTaskRespVO respVO = convert3(task); + BpmTaskExtDO taskExtDO = bpmTaskExtDOMap.get(task.getId()); + copyTo(taskExtDO, respVO); + if (processInstance != null) { + AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId())); + respVO.setProcessInstance(convert(processInstance, startUser)); + } + AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(task.getAssignee())); + if (assignUser != null) { + respVO.setAssigneeUser(convert3(assignUser)); + DeptRespDTO dept = deptMap.get(assignUser.getDeptId()); + if (dept != null) { + respVO.getAssigneeUser().setDeptName(dept.getName()); + } + } + return respVO; + }); + } + + @Mapping(source = "taskDefinitionKey", target = "definitionKey") + BpmTaskRespVO convert3(HistoricTaskInstance bean); + + BpmTaskRespVO.User convert3(AdminUserRespDTO bean); + + @Mapping(target = "id", ignore = true) + void copyTo(BpmTaskExtDO from, @MappingTarget BpmTaskDonePageItemRespVO to); + + @Mappings({@Mapping(source = "processInstance.id", target = "id"), + @Mapping(source = "processInstance.name", target = "name"), + @Mapping(source = "processInstance.startUserId", target = "startUserId"), + @Mapping(source = "processInstance.processDefinitionId", target = "processDefinitionId"), + @Mapping(source = "startUser.nickname", target = "startUserNickname")}) + BpmTaskTodoPageItemRespVO.ProcessInstance convert(HistoricProcessInstance processInstance, + AdminUserRespDTO startUser); + + default BpmTaskExtDO convert2TaskExt(Task task) { + BpmTaskExtDO taskExtDO = new BpmTaskExtDO().setTaskId(task.getId()) + .setAssigneeUserId(NumberUtils.parseLong(task.getAssignee())).setName(task.getName()) + .setProcessDefinitionId(task.getProcessDefinitionId()).setProcessInstanceId(task.getProcessInstanceId()); + taskExtDO.setCreateTime(task.getCreateTime()); + return taskExtDO; + } + + default BpmMessageSendWhenTaskCreatedReqDTO convert(ProcessInstance processInstance, AdminUserRespDTO startUser, + Task task) { + BpmMessageSendWhenTaskCreatedReqDTO reqDTO = new BpmMessageSendWhenTaskCreatedReqDTO(); + reqDTO.setProcessInstanceId(processInstance.getProcessInstanceId()) + .setProcessInstanceName(processInstance.getName()).setStartUserId(startUser.getId()) + .setStartUserNickname(startUser.getNickname()).setTaskId(task.getId()).setTaskName(task.getName()) + .setAssigneeUserId(NumberUtils.parseLong(task.getAssignee())); + return reqDTO; + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/user/DeptConvert.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/user/DeptConvert.java new file mode 100644 index 00000000..2a7dc669 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/user/DeptConvert.java @@ -0,0 +1,25 @@ +package com.ruoyi.flowable.convert.user; + +import com.ruoyi.common.core.domain.entity.SysDept; +import com.ruoyi.flowable.domain.dto.user.DeptRespDTO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; +import org.mapstruct.factory.Mappers; + +import java.util.List; + + +@Mapper +public interface DeptConvert { + + DeptConvert INSTANCE = Mappers.getMapper(DeptConvert.class); + + @Mappings({ + @Mapping(source = "deptId", target = "id"), + @Mapping(source = "deptName", target = "name") + }) + DeptRespDTO convert(SysDept bean); + + List convertList(List list); +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/user/UserConvert.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/user/UserConvert.java new file mode 100644 index 00000000..0184c341 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/convert/user/UserConvert.java @@ -0,0 +1,26 @@ +package com.ruoyi.flowable.convert.user; + +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.flowable.domain.dto.user.AdminUserRespDTO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +@Mapper +public interface UserConvert { + + UserConvert INSTANCE = Mappers.getMapper(UserConvert.class); + + @Mappings({ + @Mapping(source = "userId", target = "id"), + @Mapping(source = "nickName", target = "nickname"), + @Mapping(source = "phonenumber", target = "mobile") + }) + AdminUserRespDTO convert(SysUser bean); + + List convertList(List users); + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/enums/DictTypeConstants.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/enums/DictTypeConstants.java new file mode 100644 index 00000000..d3665cc1 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/enums/DictTypeConstants.java @@ -0,0 +1,13 @@ +package com.ruoyi.flowable.core.enums; + +/** + * BPM 字典类型的枚举类 + * + * hasPermi + */ +public interface DictTypeConstants { + + String TASK_ASSIGN_RULE_TYPE = "bpm_task_assign_rule_type"; // 任务分配规则类型 + String TASK_ASSIGN_SCRIPT = "bpm_task_assign_script"; // 任务分配自定义脚本 + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/enums/ErrorCodeConstants.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/enums/ErrorCodeConstants.java new file mode 100644 index 00000000..351903ea --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/enums/ErrorCodeConstants.java @@ -0,0 +1,65 @@ +package com.ruoyi.flowable.core.enums; + + +import com.ruoyi.common.exception.ErrorCode; + +/** + * 工作流 错误码枚举类 + * + * 工作流系统,使用 1-009-000-000 段 + */ +public interface ErrorCodeConstants { + + // ========== 通用流程处理 模块 1-009-000-000 ========== + ErrorCode HIGHLIGHT_IMG_ERROR = new ErrorCode(1009000002, "获取高亮流程图异常"); + + // ========== OA 流程模块 1-009-001-000 ========== + ErrorCode OA_LEAVE_NOT_EXISTS = new ErrorCode(1009001001, "请假申请不存在"); + ErrorCode OA_PM_POST_NOT_EXISTS = new ErrorCode(1009001002, "项目经理岗位未设置"); + ErrorCode OA_DEPART_PM_POST_NOT_EXISTS = new ErrorCode(1009001009, "部门的项目经理不存在"); + ErrorCode OA_BM_POST_NOT_EXISTS = new ErrorCode(1009001004, "部门经理岗位未设置"); + ErrorCode OA_DEPART_BM_POST_NOT_EXISTS = new ErrorCode(1009001005, "部门的部门经理不存在"); + ErrorCode OA_HR_POST_NOT_EXISTS = new ErrorCode(1009001006, "HR岗位未设置"); + ErrorCode OA_DAY_LEAVE_ERROR = new ErrorCode(1009001007, "请假天数必须>=1"); + + // ========== 流程模型 1-009-002-000 ========== + ErrorCode MODEL_KEY_EXISTS = new ErrorCode(1009002000, "已经存在流程标识为【{}】的流程"); + ErrorCode MODEL_NOT_EXISTS = new ErrorCode(1009002001, "流程模型不存在"); + ErrorCode MODEL_KEY_VALID = new ErrorCode(1009002002, "流程标识格式不正确,需要以字母或下划线开头,后接任意字母、数字、中划线、下划线、句点!"); + ErrorCode MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG = new ErrorCode(1009002003, "部署流程失败,原因:流程表单未配置,请点击【修改流程】按钮进行配置"); + ErrorCode MODEL_DEPLOY_FAIL_TASK_ASSIGN_RULE_NOT_CONFIG = new ErrorCode(1009002004, "部署流程失败," + + "原因:用户任务({})未配置分配规则,请点击【修改流程】按钮进行配置"); + ErrorCode MODEL_DEPLOY_FAIL_TASK_INFO_EQUALS = new ErrorCode(1009003005, "流程定义部署失败,原因:信息未发生变化"); + + // ========== 流程定义 1-009-003-000 ========== + ErrorCode PROCESS_DEFINITION_KEY_NOT_MATCH = new ErrorCode(1009003000, "流程定义的标识期望是({}),当前是({}),请修改 BPMN 流程图"); + ErrorCode PROCESS_DEFINITION_NAME_NOT_MATCH = new ErrorCode(1009003001, "流程定义的名字期望是({}),当前是({}),请修改 BPMN 流程图"); + ErrorCode PROCESS_DEFINITION_NOT_EXISTS = new ErrorCode(1009003002, "流程定义不存在"); + ErrorCode PROCESS_DEFINITION_IS_SUSPENDED = new ErrorCode(1009003003, "流程定义处于挂起状态"); + ErrorCode PROCESS_DEFINITION_BPMN_MODEL_NOT_EXISTS = new ErrorCode(1009003004, "流程定义的模型不存在"); + + // ========== 流程实例 1-009-004-000 ========== + ErrorCode PROCESS_INSTANCE_NOT_EXISTS = new ErrorCode(1009004000, "流程实例不存在"); + ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS = new ErrorCode(1009004001, "流程取消失败,流程不处于运行中"); + ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF = new ErrorCode(1009004002, "流程取消失败,该流程不是你发起的"); + + // ========== 流程任务 1-009-005-000 ========== + ErrorCode TASK_COMPLETE_FAIL_NOT_EXISTS = new ErrorCode(1009005000, "审批任务失败,原因:该任务不处于未审批"); + ErrorCode TASK_COMPLETE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1009005001, "审批任务失败,原因:该任务的审批人不是你"); + + // ========== 流程任务分配规则 1-009-006-000 ========== + ErrorCode TASK_ASSIGN_RULE_EXISTS = new ErrorCode(1009006000, "流程({}) 的任务({}) 已经存在分配规则"); + ErrorCode TASK_ASSIGN_RULE_NOT_EXISTS = new ErrorCode(1009006001, "流程任务分配规则不存在"); + ErrorCode TASK_UPDATE_FAIL_NOT_MODEL = new ErrorCode(1009006002, "只有流程模型的任务分配规则,才允许被修改"); + ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1009006003, "操作失败,原因:找不到任务的审批人!"); + ErrorCode TASK_ASSIGN_SCRIPT_NOT_EXISTS = new ErrorCode(1009006004, "操作失败,原因:任务分配脚本({}) 不存在"); + + // ========== 动态表单模块 1-009-010-000 ========== + ErrorCode FORM_NOT_EXISTS = new ErrorCode(1009010000, "动态表单不存在"); + ErrorCode FORM_FIELD_REPEAT = new ErrorCode(1009010001, "表单项({}) 和 ({}) 使用了相同的字段名({})"); + + // ========== 用户组模块 1-009-011-000 ========== + ErrorCode USER_GROUP_NOT_EXISTS = new ErrorCode(1009011000, "用户组不存在"); + ErrorCode USER_GROUP_IS_DISABLE = new ErrorCode(1009011001, "名字为【{}】的用户组已被禁用"); + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/enums/definition/BpmModelFormTypeEnum.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/enums/definition/BpmModelFormTypeEnum.java new file mode 100644 index 00000000..c1ce540f --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/enums/definition/BpmModelFormTypeEnum.java @@ -0,0 +1,21 @@ +package com.ruoyi.flowable.core.enums.definition; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * BPM 模型的表单类型的枚举 + * + * hasPermi + */ +@Getter +@AllArgsConstructor +public enum BpmModelFormTypeEnum { + + NORMAL(10, "流程表单"), // 对应 BpmFormDO + CUSTOM(20, "业务表单") // 业务自己定义的表单,自己进行数据的存储 + ; + + private final Integer type; + private final String desc; +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/enums/definition/BpmTaskAssignRuleTypeEnum.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/enums/definition/BpmTaskAssignRuleTypeEnum.java new file mode 100644 index 00000000..cfc1ac0d --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/enums/definition/BpmTaskAssignRuleTypeEnum.java @@ -0,0 +1,34 @@ +package com.ruoyi.flowable.core.enums.definition; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * BPM 任务分配规则的类型枚举 + * + * hasPermi + */ +@Getter +@AllArgsConstructor +public enum BpmTaskAssignRuleTypeEnum { + + ROLE(10, "角色"), + DEPT_MEMBER(20, "部门的成员"), // 包括负责人 + DEPT_LEADER(21, "部门的负责人"), + POST(22, "岗位"), + USER(30, "用户"), + USER_GROUP(40, "用户组"), + SCRIPT(50, "自定义脚本"), // 例如说,发起人所在部门的领导、发起人所在部门的领导的领导 + VARIABLE(60, "流程变量"), // 目前固定取流程key+"_assignees",FlowableUtils.formatCollectionVariable(execution.getCurrentActivityId()) + ; + + /** + * 类型 + */ + private final Integer type; + /** + * 描述 + */ + private final String desc; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/enums/definition/BpmTaskRuleScriptEnum.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/enums/definition/BpmTaskRuleScriptEnum.java new file mode 100644 index 00000000..0aee6396 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/enums/definition/BpmTaskRuleScriptEnum.java @@ -0,0 +1,30 @@ +package com.ruoyi.flowable.core.enums.definition; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * BPM 任务规则的脚本枚举 + * 目前暂时通过 TODO 芋艿:硬编码,未来可以考虑 Groovy 动态脚本的方式 + * + * hasPermi + */ +@Getter +@AllArgsConstructor +public enum BpmTaskRuleScriptEnum { + + START_USER(10L, "流程发起人"), + + LEADER_X1(20L, "流程发起人的一级领导"), + LEADER_X2(21L, "流程发起人的二级领导"); + + /** + * 脚本编号 + */ + private final Long id; + /** + * 脚本描述 + */ + private final String desc; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/enums/message/BpmMessageEnum.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/enums/message/BpmMessageEnum.java new file mode 100644 index 00000000..cf806a6b --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/enums/message/BpmMessageEnum.java @@ -0,0 +1,26 @@ +package com.ruoyi.flowable.core.enums.message; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * Bpm 消息的枚举 + * + * hasPermi + */ +@AllArgsConstructor +@Getter +public enum BpmMessageEnum { + + PROCESS_INSTANCE_APPROVE("bpm_process_instance_approve"), // 流程任务被审批通过时,发送给申请人 + PROCESS_INSTANCE_REJECT("bpm_process_instance_reject"), // 流程任务被审批不通过时,发送给申请人 + TASK_ASSIGNED("bpm_task_assigned"); // 任务被分配时,发送给审批人 + + /** + * 短信模板的标识 + * + * 关联 SmsTemplateDO 的 code 属性 + */ + private final String smsTemplateCode; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/enums/task/BpmProcessInstanceDeleteReasonEnum.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/enums/task/BpmProcessInstanceDeleteReasonEnum.java new file mode 100644 index 00000000..a64b0e7c --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/enums/task/BpmProcessInstanceDeleteReasonEnum.java @@ -0,0 +1,58 @@ +package com.ruoyi.flowable.core.enums.task; + +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 流程实例的删除原因 + * + * hasPermi + */ +@Getter +@AllArgsConstructor +public enum BpmProcessInstanceDeleteReasonEnum { + + REJECT_TASK("不通过任务,原因:{}"), // 修改文案时,需要注意 isRejectReason 方法 + CANCEL_TASK("主动取消任务,原因:{}"), + + // ========== 流程任务的独有原因 ========== + MULTI_TASK_END("系统自动取消,原因:多任务审批已经满足条件,无需审批该任务"), // 多实例满足 condition 而结束时,其它任务实例任务会被取消,对应的删除原因是 MI_END + + ; + + private final String reason; + + /** + * 格式化理由 + * + * @param args 参数 + * @return 理由 + */ + public String format(Object... args) { + return StrUtil.format(reason, args); + } + + // ========== 逻辑 ========== + + public static boolean isRejectReason(String reason) { + return StrUtil.startWith(reason, "不通过任务,原因:"); + } + + /** + * 将 Flowable 的删除原因,翻译成对应的中文原因 + * + * @param reason 原始原因 + * @return 原因 + */ + public static String translateReason(String reason) { + if (StrUtil.isEmpty(reason)) { + return reason; + } + switch (reason) { + case "MI_END": return MULTI_TASK_END.getReason(); + default: return reason; + } + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/enums/task/BpmProcessInstanceResultEnum.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/enums/task/BpmProcessInstanceResultEnum.java new file mode 100644 index 00000000..f594db69 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/enums/task/BpmProcessInstanceResultEnum.java @@ -0,0 +1,48 @@ +package com.ruoyi.flowable.core.enums.task; + +import com.ruoyi.common.utils.ObjectUtils; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 流程实例的结果 + * + * @author jason + */ +@Getter +@AllArgsConstructor +public enum BpmProcessInstanceResultEnum { + + PROCESS(1, "处理中"), + APPROVE(2, "通过"), + REJECT(3, "驳回"), + CANCEL(4, "已取消"), + + // ========== 流程任务独有的状态 ========== + + BACK(5, "退回/驳回"); + + /** + * 结果 + * + * 如果新增时,注意 {@link #isEndResult(Integer)} 是否需要变更 + */ + private final Integer result; + /** + * 描述 + */ + private final String desc; + + /** + * 判断该结果是否已经处于 End 最终结果 + * + * 主要用于一些结果更新的逻辑,如果已经是最终结果,就不再进行更新 + * + * @param result 结果 + * @return 是否 + */ + public static boolean isEndResult(Integer result) { + return ObjectUtils.equalsAny(result, APPROVE.getResult(), REJECT.getResult(), CANCEL.getResult(), BACK.getResult()); + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/enums/task/BpmProcessInstanceStatusEnum.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/enums/task/BpmProcessInstanceStatusEnum.java new file mode 100644 index 00000000..1210fd26 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/enums/task/BpmProcessInstanceStatusEnum.java @@ -0,0 +1,27 @@ +package com.ruoyi.flowable.core.enums.task; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 流程实例的状态 + * + * hasPermi + */ +@Getter +@AllArgsConstructor +public enum BpmProcessInstanceStatusEnum { + + RUNNING(1, "进行中"), + FINISH(2, "已完成"); + + /** + * 状态 + */ + private final Integer status; + /** + * 描述 + */ + private final String desc; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/enums/user/ErrorCodeConstants.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/enums/user/ErrorCodeConstants.java new file mode 100644 index 00000000..195294f0 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/enums/user/ErrorCodeConstants.java @@ -0,0 +1,28 @@ +package com.ruoyi.flowable.core.enums.user; + + +import com.ruoyi.common.exception.ErrorCode; + +/** + * Member 错误码枚举类 + *

+ * member 系统,使用 1-004-000-000 段 + */ +public interface ErrorCodeConstants { + + // ========== 用户相关 1004001000============ + ErrorCode USER_NOT_EXISTS = new ErrorCode(1004001000, "用户不存在"); + ErrorCode USER_PASSWORD_FAILED = new ErrorCode(1004001001, "密码校验失败"); + + // ========== AUTH 模块 1004003000 ========== + ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1004003000, "登录失败,账号密码不正确"); + ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1004003001, "登录失败,账号被禁用"); + ErrorCode AUTH_TOKEN_EXPIRED = new ErrorCode(1004003004, "Token 已经过期"); + ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1004003005, "未绑定账号,需要进行绑定"); + ErrorCode AUTH_WEIXIN_MINI_APP_PHONE_CODE_ERROR = new ErrorCode(1004003006, "获得手机号失败"); + + // ========== 用户收件地址 1004004000 ========== + ErrorCode ADDRESS_NOT_EXISTS = new ErrorCode(1004004000, "用户收件地址不存在"); + ErrorCode ADDRESS_FORBIDDEN = new ErrorCode(1004004001, "没有该操作权限"); + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/enums/user/SysErrorCodeConstants.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/enums/user/SysErrorCodeConstants.java new file mode 100644 index 00000000..bc65ec64 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/enums/user/SysErrorCodeConstants.java @@ -0,0 +1,146 @@ +package com.ruoyi.flowable.core.enums.user; + + +import com.ruoyi.common.exception.ErrorCode; + +/** + * System 错误码枚举类 + *

+ * system 系统,使用 1-002-000-000 段 + */ +public interface SysErrorCodeConstants { + + // ========== AUTH 模块 1002000000 ========== + ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1002000000, "登录失败,账号密码不正确"); + ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1002000001, "登录失败,账号被禁用"); + ErrorCode AUTH_LOGIN_CAPTCHA_NOT_FOUND = new ErrorCode(1002000003, "验证码不存在"); + ErrorCode AUTH_LOGIN_CAPTCHA_CODE_ERROR = new ErrorCode(1002000004, "验证码不正确"); + ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1002000005, "未绑定账号,需要进行绑定"); + ErrorCode AUTH_TOKEN_EXPIRED = new ErrorCode(1002000006, "Token 已经过期"); + ErrorCode AUTH_MOBILE_NOT_EXISTS = new ErrorCode(1002000007, "手机号不存在"); + + // ========== 菜单模块 1002001000 ========== + ErrorCode MENU_NAME_DUPLICATE = new ErrorCode(1002001000, "已经存在该名字的菜单"); + ErrorCode MENU_PARENT_NOT_EXISTS = new ErrorCode(1002001001, "父菜单不存在"); + ErrorCode MENU_PARENT_ERROR = new ErrorCode(1002001002, "不能设置自己为父菜单"); + ErrorCode MENU_NOT_EXISTS = new ErrorCode(1002001003, "菜单不存在"); + ErrorCode MENU_EXISTS_CHILDREN = new ErrorCode(1002001004, "存在子菜单,无法删除"); + ErrorCode MENU_PARENT_NOT_DIR_OR_MENU = new ErrorCode(1002001005, "父菜单的类型必须是目录或者菜单"); + + // ========== 角色模块 1002002000 ========== + ErrorCode ROLE_NOT_EXISTS = new ErrorCode(1002002000, "角色不存在"); + ErrorCode ROLE_NAME_DUPLICATE = new ErrorCode(1002002001, "已经存在名为【{}】的角色"); + ErrorCode ROLE_CODE_DUPLICATE = new ErrorCode(1002002002, "已经存在编码为【{}】的角色"); + ErrorCode ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE = new ErrorCode(1002002003, "不能操作类型为系统内置的角色"); + ErrorCode ROLE_IS_DISABLE = new ErrorCode(1002002004, "名字为【{}】的角色已被禁用"); + ErrorCode ROLE_ADMIN_CODE_ERROR = new ErrorCode(1002002005, "编码【{}】不能使用"); + + // ========== 用户模块 1002003000 ========== + ErrorCode USER_USERNAME_EXISTS = new ErrorCode(1002003000, "用户账号已经存在"); + ErrorCode USER_MOBILE_EXISTS = new ErrorCode(1002003001, "手机号已经存在"); + ErrorCode USER_EMAIL_EXISTS = new ErrorCode(1002003002, "邮箱已经存在"); + ErrorCode USER_NOT_EXISTS = new ErrorCode(1002003003, "用户不存在"); + ErrorCode USER_IMPORT_LIST_IS_EMPTY = new ErrorCode(1002003004, "导入用户数据不能为空!"); + ErrorCode USER_PASSWORD_FAILED = new ErrorCode(1002003005, "用户密码校验失败"); + ErrorCode USER_IS_DISABLE = new ErrorCode(1002003006, "名字为【{}】的用户已被禁用"); + ErrorCode USER_COUNT_MAX = new ErrorCode(1002003008, "创建用户失败,原因:超过租户最大租户配额({})!"); + + // ========== 部门模块 1002004000 ========== + ErrorCode DEPT_NAME_DUPLICATE = new ErrorCode(1002004000, "已经存在该名字的部门"); + ErrorCode DEPT_PARENT_NOT_EXITS = new ErrorCode(1002004001, "父级部门不存在"); + ErrorCode DEPT_NOT_FOUND = new ErrorCode(1002004002, "当前部门不存在"); + ErrorCode DEPT_EXITS_CHILDREN = new ErrorCode(1002004003, "存在子部门,无法删除"); + ErrorCode DEPT_PARENT_ERROR = new ErrorCode(1002004004, "不能设置自己为父部门"); + ErrorCode DEPT_EXISTS_USER = new ErrorCode(1002004005, "部门中存在员工,无法删除"); + ErrorCode DEPT_NOT_ENABLE = new ErrorCode(1002004006, "部门不处于开启状态,不允许选择"); + ErrorCode DEPT_PARENT_IS_CHILD = new ErrorCode(1002004007, "不能设置自己的子部门为父部门"); + + // ========== 岗位模块 1002005000 ========== + ErrorCode POST_NOT_FOUND = new ErrorCode(1002005000, "当前岗位不存在"); + ErrorCode POST_NOT_ENABLE = new ErrorCode(1002005001, "岗位({}) 不处于开启状态,不允许选择"); + ErrorCode POST_NAME_DUPLICATE = new ErrorCode(1002005002, "已经存在该名字的岗位"); + ErrorCode POST_CODE_DUPLICATE = new ErrorCode(1002005003, "已经存在该标识的岗位"); + + // ========== 字典类型 1002006000 ========== + ErrorCode DICT_TYPE_NOT_EXISTS = new ErrorCode(1002006001, "当前字典类型不存在"); + ErrorCode DICT_TYPE_NOT_ENABLE = new ErrorCode(1002006002, "字典类型不处于开启状态,不允许选择"); + ErrorCode DICT_TYPE_NAME_DUPLICATE = new ErrorCode(1002006003, "已经存在该名字的字典类型"); + ErrorCode DICT_TYPE_TYPE_DUPLICATE = new ErrorCode(1002006004, "已经存在该类型的字典类型"); + ErrorCode DICT_TYPE_HAS_CHILDREN = new ErrorCode(1002006005, "无法删除,该字典类型还有字典数据"); + + // ========== 字典数据 1002007000 ========== + ErrorCode DICT_DATA_NOT_EXISTS = new ErrorCode(1002007001, "当前字典数据不存在"); + ErrorCode DICT_DATA_NOT_ENABLE = new ErrorCode(1002007002, "字典数据({})不处于开启状态,不允许选择"); + ErrorCode DICT_DATA_VALUE_DUPLICATE = new ErrorCode(1002007003, "已经存在该值的字典数据"); + + // ========== 通知公告 1002008000 ========== + ErrorCode NOTICE_NOT_FOUND = new ErrorCode(1002008001, "当前通知公告不存在"); + + // ========== 短信渠道 1002011000 ========== + ErrorCode SMS_CHANNEL_NOT_EXISTS = new ErrorCode(1002011000, "短信渠道不存在"); + ErrorCode SMS_CHANNEL_DISABLE = new ErrorCode(1002011001, "短信渠道不处于开启状态,不允许选择"); + ErrorCode SMS_CHANNEL_HAS_CHILDREN = new ErrorCode(1002011002, "无法删除,该短信渠道还有短信模板"); + + // ========== 短信模板 1002012000 ========== + ErrorCode SMS_TEMPLATE_NOT_EXISTS = new ErrorCode(1002012000, "短信模板不存在"); + ErrorCode SMS_TEMPLATE_CODE_DUPLICATE = new ErrorCode(1002012001, "已经存在编码为【{}】的短信模板"); + + // ========== 短信发送 1002013000 ========== + ErrorCode SMS_SEND_MOBILE_NOT_EXISTS = new ErrorCode(1002013000, "手机号不存在"); + ErrorCode SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS = new ErrorCode(1002013001, "模板参数({})缺失"); + ErrorCode SMS_SEND_TEMPLATE_NOT_EXISTS = new ErrorCode(1002013002, "短信模板不存在"); + + // ========== 短信验证码 1002014000 ========== + ErrorCode SMS_CODE_NOT_FOUND = new ErrorCode(1002014000, "验证码不存在"); + ErrorCode SMS_CODE_EXPIRED = new ErrorCode(1002014001, "验证码已过期"); + ErrorCode SMS_CODE_USED = new ErrorCode(1002014002, "验证码已使用"); + ErrorCode SMS_CODE_NOT_CORRECT = new ErrorCode(1002014003, "验证码不正确"); + ErrorCode SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY = new ErrorCode(1002014004, "超过每日短信发送数量"); + ErrorCode SMS_CODE_SEND_TOO_FAST = new ErrorCode(1002014005, "短信发送过于频率"); + ErrorCode SMS_CODE_IS_EXISTS = new ErrorCode(1002014006, "手机号已被使用"); + ErrorCode SMS_CODE_IS_UNUSED = new ErrorCode(1002014007, "验证码未被使用"); + + // ========== 租户信息 1002015000 ========== + ErrorCode TENANT_NOT_EXISTS = new ErrorCode(1002015000, "租户不存在"); + ErrorCode TENANT_DISABLE = new ErrorCode(1002015001, "名字为【{}】的租户已被禁用"); + ErrorCode TENANT_EXPIRE = new ErrorCode(1002015002, "名字为【{}】的租户已过期"); + ErrorCode TENANT_CAN_NOT_UPDATE_SYSTEM = new ErrorCode(1002015003, "系统租户不能进行修改、删除等操作!"); + + // ========== 租户套餐 1002016000 ========== + ErrorCode TENANT_PACKAGE_NOT_EXISTS = new ErrorCode(1002016000, "租户套餐不存在"); + ErrorCode TENANT_PACKAGE_USED = new ErrorCode(1002016001, "租户正在使用该套餐,请给租户重新设置套餐后再尝试删除"); + ErrorCode TENANT_PACKAGE_DISABLE = new ErrorCode(1002016002, "名字为【{}】的租户套餐已被禁用"); + + // ========== 错误码模块 1002017000 ========== + ErrorCode ERROR_CODE_NOT_EXISTS = new ErrorCode(1002017000, "错误码不存在"); + ErrorCode ERROR_CODE_DUPLICATE = new ErrorCode(1002017001, "已经存在编码为【{}】的错误码"); + + // ========== 社交用户 1002018000 ========== + ErrorCode SOCIAL_USER_AUTH_FAILURE = new ErrorCode(1002018000, "社交授权失败,原因是:{}"); + ErrorCode SOCIAL_USER_UNBIND_NOT_SELF = new ErrorCode(1002018001, "社交解绑失败,非当前用户绑定"); + ErrorCode SOCIAL_USER_NOT_FOUND = new ErrorCode(1002018002, "社交授权失败,找不到对应的用户"); + + // ========== 系统敏感词 1002019000 ========= + ErrorCode SENSITIVE_WORD_NOT_EXISTS = new ErrorCode(1002019000, "系统敏感词在所有标签中都不存在"); + ErrorCode SENSITIVE_WORD_EXISTS = new ErrorCode(1002019001, "系统敏感词已在标签中存在"); + + // ========== OAuth2 客户端 1002020000 ========= + ErrorCode OAUTH2_CLIENT_NOT_EXISTS = new ErrorCode(1002020000, "OAuth2 客户端不存在"); + ErrorCode OAUTH2_CLIENT_EXISTS = new ErrorCode(1002020001, "OAuth2 客户端编号已存在"); + ErrorCode OAUTH2_CLIENT_DISABLE = new ErrorCode(1002020002, "OAuth2 客户端已禁用"); + ErrorCode OAUTH2_CLIENT_AUTHORIZED_GRANT_TYPE_NOT_EXISTS = new ErrorCode(1002020003, "不支持该授权类型"); + ErrorCode OAUTH2_CLIENT_SCOPE_OVER = new ErrorCode(1002020004, "授权范围过大"); + ErrorCode OAUTH2_CLIENT_REDIRECT_URI_NOT_MATCH = new ErrorCode(1002020005, "无效 redirect_uri: {}"); + ErrorCode OAUTH2_CLIENT_CLIENT_SECRET_ERROR = new ErrorCode(1002020006, "无效 client_secret: {}"); + + // ========== OAuth2 授权 1002021000 ========= + ErrorCode OAUTH2_GRANT_CLIENT_ID_MISMATCH = new ErrorCode(1002021000, "client_id 不匹配"); + ErrorCode OAUTH2_GRANT_REDIRECT_URI_MISMATCH = new ErrorCode(1002021001, "redirect_uri 不匹配"); + ErrorCode OAUTH2_GRANT_STATE_MISMATCH = new ErrorCode(1002021002, "state 不匹配"); + ErrorCode OAUTH2_GRANT_CODE_NOT_EXISTS = new ErrorCode(1002021003, "code 不存在"); + + // ========== OAuth2 授权 1002022000 ========= + ErrorCode OAUTH2_CODE_NOT_EXISTS = new ErrorCode(1002022000, "code 不存在"); + ErrorCode OAUTH2_CODE_EXPIRE = new ErrorCode(1002022000, "code 已过期"); + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/utils/FlowableUtils.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/utils/FlowableUtils.java new file mode 100644 index 00000000..6401e6f5 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/utils/FlowableUtils.java @@ -0,0 +1,84 @@ +package com.ruoyi.flowable.core.utils; + +import org.flowable.bpmn.converter.BpmnXMLConverter; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.bpmn.model.FlowElement; +import org.flowable.common.engine.impl.identity.Authentication; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Flowable 相关的工具方法 + * + * hasPermi + */ +public class FlowableUtils { + + public static String assignees = "_assignees";; + + // ========== User 相关的工具方法 ========== + public static void setAuthenticatedUserId(Long userId) { + Authentication.setAuthenticatedUserId(String.valueOf(userId)); + } + + public static void clearAuthenticatedUserId() { + Authentication.setAuthenticatedUserId(null); + } + + // ========== BPMN 相关的工具方法 ========== + + /** + * 获得 BPMN 流程中,指定的元素们 + * + * @param model + * @param clazz 指定元素。例如说,{@link org.flowable.bpmn.model.UserTask}、{@link org.flowable.bpmn.model.Gateway} 等等 + * @return 元素们 + */ + public static List getBpmnModelElements(BpmnModel model, Class clazz) { + List result = new ArrayList<>(); + model.getProcesses().forEach(process -> { + process.getFlowElements().forEach(flowElement -> { + if (flowElement.getClass().isAssignableFrom(clazz)) { + result.add((T) flowElement); + } + }); + }); + return result; + } + + /** + * 比较 两个bpmnModel 是否相同 + * @param oldModel 老的bpmn model + * @param newModel 新的bpmn model + */ + public static boolean equals(BpmnModel oldModel, BpmnModel newModel) { + // 由于 BpmnModel 未提供 equals 方法,所以只能转成字节数组,进行比较 + return Arrays.equals(getBpmnBytes(oldModel), getBpmnBytes(newModel)); + } + + /** + * 把 bpmnModel 转换成 byte[] + * @param model bpmnModel + */ + public static byte[] getBpmnBytes(BpmnModel model) { + if (model == null) { + return new byte[0]; + } + BpmnXMLConverter converter = new BpmnXMLConverter(); + return converter.convertToXML(model); + } + + // ========== Execution 相关的工具方法 ========== + + public static String formatCollectionVariable(String activityId) { + + return activityId + assignees; + } + + public static String formatCollectionElementVariable(String activityId) { + return activityId + "_assignee"; + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/web/FlowableWebFilter.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/web/FlowableWebFilter.java new file mode 100644 index 00000000..c3ee83f8 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/core/web/FlowableWebFilter.java @@ -0,0 +1,37 @@ +package com.ruoyi.flowable.core.web; + +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.flowable.core.utils.FlowableUtils; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * flowable Web 过滤器,将 userId 设置到 {@link org.flowable.common.engine.impl.identity.Authentication} 中 + * + * @author jason + */ +public class FlowableWebFilter extends OncePerRequestFilter { + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) + throws ServletException, IOException { + try { + // 设置工作流的用户 + Long userId = SecurityUtils.getLoginUserId(); + if (userId != null) { + FlowableUtils.setAuthenticatedUserId(userId); + } + // 过滤 + chain.doFilter(request, response); + } finally { + // 清理 + FlowableUtils.clearAuthenticatedUserId(); + } + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/definition/BpmFormFieldRespDTO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/definition/BpmFormFieldRespDTO.java new file mode 100644 index 00000000..192667f4 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/definition/BpmFormFieldRespDTO.java @@ -0,0 +1,27 @@ +package com.ruoyi.flowable.domain.dto.definition; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * Bpm 表单的 Field 表单项 Response DTO + * 字段的定义,可见 https://github.com/JakHuang/form-generator/issues/46 文档 + * + * hasPermi + */ +@Data +@Accessors(chain = true) +public class BpmFormFieldRespDTO { + + /** + * 表单标题 + */ + private String label; + /** + * 表单字段的属性名,可自定义 + */ + @JsonProperty(value = "vModel") + private String vModel; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/definition/BpmModelMetaInfoRespDTO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/definition/BpmModelMetaInfoRespDTO.java new file mode 100644 index 00000000..4afa340b --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/definition/BpmModelMetaInfoRespDTO.java @@ -0,0 +1,39 @@ +package com.ruoyi.flowable.domain.dto.definition; + +import com.ruoyi.flowable.core.enums.definition.BpmModelFormTypeEnum; +import lombok.Data; + +/** + * BPM 流程 MetaInfo Response DTO + * 主要用于 { Model#setMetaInfo(String)} 的存储 + * + * hasPermi + */ +@Data +public class BpmModelMetaInfoRespDTO { + + /** + * 流程描述 + */ + private String description; + /** + * 表单类型 + */ + private Integer formType; + /** + * 表单编号 + * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时 + */ + private Long formId; + /** + * 自定义表单的提交路径,使用 Vue 的路由地址 + * 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时 + */ + private String formCustomCreatePath; + /** + * 自定义表单的查看路径,使用 Vue 的路由地址 + * 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时 + */ + private String formCustomViewPath; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/definition/BpmProcessDefinitionCreateReqDTO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/definition/BpmProcessDefinitionCreateReqDTO.java new file mode 100644 index 00000000..f1780f74 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/definition/BpmProcessDefinitionCreateReqDTO.java @@ -0,0 +1,106 @@ +package com.ruoyi.flowable.domain.dto.definition; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.ruoyi.flowable.core.enums.definition.BpmModelFormTypeEnum; +import lombok.Data; +import lombok.experimental.Accessors; + +import javax.validation.constraints.AssertTrue; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.util.List; +import java.util.Objects; + +/** + * 流程定义创建 Request DTO + */ +@Data +@Accessors(chain = true) +public class BpmProcessDefinitionCreateReqDTO { + + // ========== 模型相关 ========== + + /** + * 流程模型的编号 + */ + @NotEmpty(message = "流程模型编号不能为空") + private String modelId; + /** + * 流程标识 + */ + @NotEmpty(message = "流程标识不能为空") + private String key; + /** + * 流程名称 + */ + @NotEmpty(message = "流程名称不能为空") + private String name; + /** + * 流程描述 + */ + private String description; + /** + * 流程分类 + * 参见 bpm_model_category 数据字典 + */ + @NotEmpty(message = "流程分类不能为空") + private String category; + /** + * BPMN XML + */ + @NotEmpty(message = "BPMN XML 不能为空") + private byte[] bpmnBytes; + + // ========== 表单相关 ========== + + /** + * 表单类型 + */ + @NotNull(message = "表单类型不能为空") + private Integer formType; + /** + * 动态表单编号 + * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时 + */ + private Long formId; + /** + * 表单的配置 + * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时 + */ + private String formConf; + /** + * 表单项的数组 + * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时 + */ + private List formFields; + /** + * 自定义表单的提交路径,使用 Vue 的路由地址 + * 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时 + */ + private String formCustomCreatePath; + /** + * 自定义表单的查看路径,使用 Vue 的路由地址 + * 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时 + */ + private String formCustomViewPath; + + @AssertTrue(message = "流程表单信息不全") + public boolean isNormalFormTypeValid() { + // 如果非业务表单,则直接通过 + if (!Objects.equals(formType, BpmModelFormTypeEnum.NORMAL.getType())) { + return true; + } + return formId != null && StrUtil.isNotEmpty(formConf) && CollUtil.isNotEmpty(formFields); + } + + @AssertTrue(message = "业务表单信息不全") + public boolean isNormalCustomTypeValid() { + // 如果非业务表单,则直接通过 + if (!Objects.equals(formType, BpmModelFormTypeEnum.CUSTOM.getType())) { + return true; + } + return StrUtil.isNotEmpty(formCustomCreatePath) && StrUtil.isNotEmpty(formCustomViewPath); + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/message/BpmMessageSendWhenProcessInstanceApproveReqDTO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/message/BpmMessageSendWhenProcessInstanceApproveReqDTO.java new file mode 100644 index 00000000..fdff5466 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/message/BpmMessageSendWhenProcessInstanceApproveReqDTO.java @@ -0,0 +1,29 @@ +package com.ruoyi.flowable.domain.dto.message; + +import lombok.Data; +import lombok.experimental.Accessors; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +/** + * BPM 发送流程实例被通过 Request DTO + */ +@Data +@Accessors(chain = true) +public class BpmMessageSendWhenProcessInstanceApproveReqDTO { + + /** + * 流程实例的编号 + */ + @NotEmpty(message = "流程实例的编号不能为空") + private String processInstanceId; + /** + * 流程实例的名字 + */ + @NotEmpty(message = "流程实例的名字不能为空") + private String processInstanceName; + @NotNull(message = "发起人的用户编号") + private Long startUserId; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/message/BpmMessageSendWhenProcessInstanceRejectReqDTO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/message/BpmMessageSendWhenProcessInstanceRejectReqDTO.java new file mode 100644 index 00000000..7b583a10 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/message/BpmMessageSendWhenProcessInstanceRejectReqDTO.java @@ -0,0 +1,35 @@ +package com.ruoyi.flowable.domain.dto.message; + +import lombok.Data; +import lombok.experimental.Accessors; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +/** + * BPM 发送流程实例被不通过 Request DTO + */ +@Data +@Accessors(chain = true) +public class BpmMessageSendWhenProcessInstanceRejectReqDTO { + + /** + * 流程实例的编号 + */ + @NotEmpty(message = "流程实例的编号不能为空") + private String processInstanceId; + /** + * 流程实例的名字 + */ + @NotEmpty(message = "流程实例的名字不能为空") + private String processInstanceName; + @NotNull(message = "发起人的用户编号") + private Long startUserId; + + /** + * 不通过理由 + */ + @NotEmpty(message = "不通过理由不能为空") + private String reason; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/message/BpmMessageSendWhenTaskCreatedReqDTO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/message/BpmMessageSendWhenTaskCreatedReqDTO.java new file mode 100644 index 00000000..b39fee36 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/message/BpmMessageSendWhenTaskCreatedReqDTO.java @@ -0,0 +1,48 @@ +package com.ruoyi.flowable.domain.dto.message; + +import lombok.Data; +import lombok.experimental.Accessors; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +/** + * BPM 发送任务被分配 Request DTO + */ +@Data +@Accessors(chain = true) +public class BpmMessageSendWhenTaskCreatedReqDTO { + + /** + * 流程实例的编号 + */ + @NotEmpty(message = "流程实例的编号不能为空") + private String processInstanceId; + /** + * 流程实例的名字 + */ + @NotEmpty(message = "流程实例的名字不能为空") + private String processInstanceName; + @NotNull(message = "发起人的用户编号") + private Long startUserId; + @NotEmpty(message = "发起人的昵称") + private String startUserNickname; + + /** + * 流程任务的编号 + */ + @NotEmpty(message = "流程任务的编号不能为空") + private String taskId; + /** + * 流程任务的名字 + */ + @NotEmpty(message = "流程任务的名字不能为空") + private String taskName; + + /** + * 审批人的用户编号 + */ + @NotNull(message = "审批人的用户编号不能为空") + private Long assigneeUserId; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/send/SmsSendSingleToUserReqDTO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/send/SmsSendSingleToUserReqDTO.java new file mode 100644 index 00000000..2aff7c41 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/send/SmsSendSingleToUserReqDTO.java @@ -0,0 +1,34 @@ +package com.ruoyi.flowable.domain.dto.send; + +import lombok.Data; + +import javax.validation.constraints.NotEmpty; +import java.util.Map; + +/** + * 短信发送给 Admin 或者 Member 用户 + * + * hasPermi + */ +@Data +public class SmsSendSingleToUserReqDTO { + + /** + * 用户编号 + */ + private Long userId; + /** + * 手机号 + */ + private String mobile; + /** + * 短信模板编号 + */ + @NotEmpty(message = "短信模板编号不能为空") + private String templateCode; + /** + * 短信模板参数 + */ + private Map templateParams; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/task/BpmProcessInstanceCreateReqDTO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/task/BpmProcessInstanceCreateReqDTO.java new file mode 100644 index 00000000..25bdc5e2 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/task/BpmProcessInstanceCreateReqDTO.java @@ -0,0 +1,35 @@ +package com.ruoyi.flowable.domain.dto.task; + +import lombok.Data; +import lombok.experimental.Accessors; + +import javax.validation.constraints.NotEmpty; +import java.util.Map; + +/** + * 流程实例的创建 Request DTO + * + * hasPermi + */ +@Data +@Accessors(chain = true) +public class BpmProcessInstanceCreateReqDTO { + + /** + * 流程定义的标识 + */ + @NotEmpty(message = "流程定义的标识不能为空") + private String processDefinitionKey; + /** + * 变量实例 + */ + private Map variables; + + /** + * 业务的唯一标识 + * + * 例如说,请假申请的编号。通过它,可以查询到对应的实例 + */ + @NotEmpty(message = "业务的唯一标识") + private String businessKey; +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/user/AdminUserRespDTO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/user/AdminUserRespDTO.java new file mode 100644 index 00000000..dba9811f --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/user/AdminUserRespDTO.java @@ -0,0 +1,44 @@ +package com.ruoyi.flowable.domain.dto.user; + +import com.ruoyi.common.enums.CommonStatusEnum; +import lombok.Data; + +import java.util.Set; + +/** + * Admin 用户 Response DTO + * + * hasPermi + */ +@Data +public class AdminUserRespDTO { + + /** + * 用户ID + */ + private Long id; + /** + * 用户昵称 + */ + private String nickname; + /** + * 帐号状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + + /** + * 部门ID + */ + private Long deptId; + /** + * 岗位编号数组 + */ + private Set postIds; + /** + * 手机号码 + */ + private String mobile; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/user/DeptRespDTO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/user/DeptRespDTO.java new file mode 100644 index 00000000..699213a5 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/user/DeptRespDTO.java @@ -0,0 +1,37 @@ +package com.ruoyi.flowable.domain.dto.user; + +import com.ruoyi.common.enums.CommonStatusEnum; +import lombok.Data; + +/** + * 部门 Response DTO + * + * hasPermi + */ +@Data +public class DeptRespDTO { + + /** + * 部门编号 + */ + private Long id; + /** + * 部门名称 + */ + private String name; + /** + * 父部门编号 + */ + private Long parentId; + /** + * 负责人的用户编号 + */ + private Long leaderUserId; + /** + * 部门状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/entity/definition/BpmFormDO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/entity/definition/BpmFormDO.java new file mode 100644 index 00000000..835deec6 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/entity/definition/BpmFormDO.java @@ -0,0 +1,58 @@ +package com.ruoyi.flowable.domain.entity.definition; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import com.ruoyi.common.mybatis.dataobject.BaseDO; +import lombok.*; + +import java.util.List; + +/** + * 工作流的表单定义 + * 用于工作流的申请表单,需要动态配置的场景 + * + * hasPermi + */ +@TableName(value = "bpm_form", autoResultMap = true) +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BpmFormDO extends BaseDO { + + /** + * 编号 + */ + @TableId(type = IdType.AUTO) + private Long id; + /** + * 表单名 + */ + private String name; + /** + * 状态 + */ + private Integer status; + /** + * 表单的配置 + */ + private String conf; + /** + * 表单项的数组 + * + * 目前直接将 https://github.com/JakHuang/form-generator 生成的 JSON 串,直接保存 + * 定义:https://github.com/JakHuang/form-generator/issues/46 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List fields; + /** + * 备注 + */ + private String remark; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/entity/definition/BpmProcessDefinitionExtDO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/entity/definition/BpmProcessDefinitionExtDO.java new file mode 100644 index 00000000..4d5f9df3 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/entity/definition/BpmProcessDefinitionExtDO.java @@ -0,0 +1,91 @@ +package com.ruoyi.flowable.domain.entity.definition; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import com.ruoyi.common.mybatis.dataobject.BaseDO; +import com.ruoyi.flowable.core.enums.definition.BpmModelFormTypeEnum; +import lombok.*; + +import java.util.List; + +/** + * Bpm 流程定义的拓展表 + * 主要解决 Activiti {@link ProcessDefinition} 不支持拓展字段,所以新建拓展表 + * + * hasPermi + */ +@TableName(value = "bpm_process_definition_ext", autoResultMap = true) +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BpmProcessDefinitionExtDO extends BaseDO { + + /** + * 编号 + */ + @TableId(type = IdType.AUTO) + private Long id; + /** + * 流程定义的编号 + * + * 关联 ProcessDefinition 的 id 属性 + */ + private String processDefinitionId; + /** + * 流程模型的编号 + * + * 关联 Model 的 id 属性 + */ + private String modelId; + /** + * 描述 + */ + private String description; + + /** + * 表单类型 + * + * 关联 {@link BpmModelFormTypeEnum} + */ + private Integer formType; + /** + * 动态表单编号 + * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时 + * + * 关联 {@link BpmFormDO#getId()} + */ + private Long formId; + /** + * 表单的配置 + * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时 + * + * 冗余 {@link BpmFormDO#getConf()} + */ + private String formConf; + /** + * 表单项的数组 + * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时 + * + * 冗余 {@link BpmFormDO#getFields()} ()} + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List formFields; + /** + * 自定义表单的提交路径,使用 Vue 的路由地址 + * 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时 + */ + private String formCustomCreatePath; + /** + * 自定义表单的查看路径,使用 Vue 的路由地址 + * 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时 + */ + private String formCustomViewPath; + + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/entity/definition/BpmTaskAssignRuleDO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/entity/definition/BpmTaskAssignRuleDO.java new file mode 100644 index 00000000..cb3c2abf --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/entity/definition/BpmTaskAssignRuleDO.java @@ -0,0 +1,84 @@ +package com.ruoyi.flowable.domain.entity.definition; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.ruoyi.common.handler.JsonLongSetTypeHandler; +import com.ruoyi.common.mybatis.dataobject.BaseDO; +import com.ruoyi.flowable.core.enums.definition.BpmTaskAssignRuleTypeEnum; +import com.ruoyi.flowable.core.enums.definition.BpmTaskRuleScriptEnum; +import lombok.*; + +import java.util.Set; + +/** + * Bpm 任务分配的规则表,用于自定义配置每个任务的负责人、候选人的分配规则。 + * 也就是说,废弃 BPMN 原本的 UserTask 设置的 assignee、candidateUsers 等配置,而是通过使用该规则进行计算对应的负责人。 + * + * 1. 默认情况下,{@link #processDefinitionId} 为 {@link #PROCESS_DEFINITION_ID_NULL} 值,表示贵改则与流程模型关联 + * 2. 在流程模型部署后,会将他的所有规则记录,复制出一份新部署出来的流程定义,通过设置 {@link #processDefinitionId} 为新的流程定义的编号进行关联 + * + * hasPermi + */ +@TableName(value = "bpm_task_assign_rule", autoResultMap = true) +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BpmTaskAssignRuleDO extends BaseDO { + + /** + * {@link #processDefinitionId} 空串,用于标识属于流程模型,而不属于流程定义 + */ + public static final String PROCESS_DEFINITION_ID_NULL = ""; + + /** + * 编号 + */ +@TableId(type = IdType.AUTO) + private Long id; + + /** + * 流程模型编号 + * + * 关联 Model 的 id 属性 + */ + private String modelId; + /** + * 流程定义编号 + * + * 关联 ProcessDefinition 的 id 属性 + */ + private String processDefinitionId; + /** + * 流程任务的定义 Key + * + * 关联 Task 的 taskDefinitionKey 属性 + */ + private String taskDefinitionKey; + + /** + * 规则类型 + * + * 枚举 {@link BpmTaskAssignRuleTypeEnum} + */ + @TableField("`type`") + private Integer type; + /** + * 规则值数组,一般关联指定表的编号 + * 根据 type 不同,对应的值是不同的: + * + * 1. {@link BpmTaskAssignRuleTypeEnum#ROLE} 时:角色编号 + * 2. {@link BpmTaskAssignRuleTypeEnum#DEPT_MEMBER} 时:部门编号 + * 3. {@link BpmTaskAssignRuleTypeEnum#DEPT_LEADER} 时:部门编号 + * 4. {@link BpmTaskAssignRuleTypeEnum#USER} 时:用户编号 + * 5. {@link BpmTaskAssignRuleTypeEnum#USER_GROUP} 时:用户组编号 + * 6. {@link BpmTaskAssignRuleTypeEnum#SCRIPT} 时:脚本编号,目前通过 {@link BpmTaskRuleScriptEnum#getId()} 标识 + */ + @TableField(typeHandler = JsonLongSetTypeHandler.class) + private Set options; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/entity/definition/BpmTaskMessageRuleDO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/entity/definition/BpmTaskMessageRuleDO.java new file mode 100644 index 00000000..e52b9d31 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/entity/definition/BpmTaskMessageRuleDO.java @@ -0,0 +1,5 @@ +package com.ruoyi.flowable.domain.entity.definition; + +// TODO 芋艿:先埋个坑。任务消息的配置规则。说白了,就是不同的 +public class BpmTaskMessageRuleDO { +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/entity/definition/BpmUserGroupDO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/entity/definition/BpmUserGroupDO.java new file mode 100644 index 00000000..1c1b81ba --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/entity/definition/BpmUserGroupDO.java @@ -0,0 +1,52 @@ +package com.ruoyi.flowable.domain.entity.definition; + +import com.ruoyi.common.handler.JsonLongSetTypeHandler; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.ruoyi.common.mybatis.dataobject.BaseDO; +import lombok.*; + +import java.util.Set; + +/** + * Bpm 用户组 + * + * hasPermi + */ +@TableName(value = "bpm_user_group", autoResultMap = true) +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BpmUserGroupDO extends BaseDO { + + /** + * 编号,自增 + */ + @TableId(type = IdType.AUTO) + private Long id; + /** + * 组名 + */ + private String name; + /** + * 描述 + */ + private String description; + /** + * 状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + /** + * 成员用户编号数组 + */ + @TableField(typeHandler = JsonLongSetTypeHandler.class) + private Set memberUserIds; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/entity/oa/BpmOALeaveDO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/entity/oa/BpmOALeaveDO.java new file mode 100644 index 00000000..fa671334 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/entity/oa/BpmOALeaveDO.java @@ -0,0 +1,87 @@ +package com.ruoyi.flowable.domain.entity.oa; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.ruoyi.common.mybatis.dataobject.BaseDO; +import com.ruoyi.flowable.core.enums.task.BpmProcessInstanceResultEnum; +import lombok.*; +import lombok.experimental.Accessors; + +import java.util.Date; + +/** + * OA 请假申请 DO + * + * {@link #day} 请假天数,目前先简单做。一般是分成请假上午和下午,可以是 1 整天,可以是 0.5 半天 + * + * @author jason + * hasPermi + */ +@TableName("bpm_oa_leave") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +public class BpmOALeaveDO extends BaseDO { + + /** + * 请假表单主键 + */ + @TableId(type = IdType.AUTO) + private Long id; + /** + * 申请人的用户编号 + * + * 关联 AdminUserDO 的 id 属性 + */ + private Long userId; + /** + * 请假类型 + */ + @TableField("`type`") + private String type; + /** + * 原因 + */ + private String reason; + /** + * 开始时间 + */ + private Date startTime; + /** + * 结束时间 + */ + private Date endTime; + /** + * 请假天数 + */ + private Long day; + /** + * 请假的结果 + * + * 枚举 {@link BpmProcessInstanceResultEnum} + * 考虑到简单,所以直接复用了 BpmProcessInstanceResultEnum 枚举,也可以自己定义一个枚举哈 + */ + private Integer result; + + /** + * 对应的流程编号 + * + * 关联 ProcessInstance 的 id 属性 + */ + private String processInstanceId; + /** + * 租户ID + */ + private String tenantId; + + @TableField(exist = false) + private String nickName; + @TableField(exist = false) + private Long deptId; +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/entity/task/BpmActivityDO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/entity/task/BpmActivityDO.java new file mode 100644 index 00000000..2a87b29d --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/entity/task/BpmActivityDO.java @@ -0,0 +1,104 @@ +package com.ruoyi.flowable.domain.entity.task; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +/** + * 任务流程关联表 + * + * @author kemengkai + * @create 2022-05-09 10:33 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class BpmActivityDO { + + /** + * 任务流程关联id + */ + private String id; + + /** + * 审批结果 + */ + private Integer rev; + + /** + * 任务流程部署id + */ + private String procDefId; + + /** + * 任务流程id + */ + private String processInstanceId; + + /** + * 任务执行id + */ + private String executionId; + + /** + * 任务key + */ + private String activityId; + + /** + * 任务id + */ + private String taskId; + + /** + * 调用流程id + */ + private String callProcInstId; + + /** + * 任务名称 + */ + private String activityName; + + /** + * 任务类型 + */ + private String activityType; + + /** + * 任务审批人id + */ + private String assignee; + + /** + * 任务开始时间 + */ + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date startTime; + + /** + * 任务结束时间 + */ + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date endTime; + + private Integer transactionOrder; + + private Long duration; + + /** + * 删除结果 + */ + private String deleteReason; + + /** + * 租户id + */ + private String tenantId; +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/entity/task/BpmProcessInstanceExtDO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/entity/task/BpmProcessInstanceExtDO.java new file mode 100644 index 00000000..3e1fc67e --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/entity/task/BpmProcessInstanceExtDO.java @@ -0,0 +1,93 @@ +package com.ruoyi.flowable.domain.entity.task; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import com.ruoyi.common.mybatis.dataobject.BaseDO; +import com.ruoyi.flowable.core.enums.task.BpmProcessInstanceResultEnum; +import com.ruoyi.flowable.core.enums.task.BpmProcessInstanceStatusEnum; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import lombok.experimental.Accessors; + +import java.util.Date; +import java.util.Map; + +/** + * Bpm 流程实例的拓展表 + * 主要解决 Activiti ProcessInstance 和 HistoricProcessInstance 不支持拓展字段,所以新建拓展表 + * + * hasPermi + */ +@TableName(value = "bpm_process_instance_ext", autoResultMap = true) +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Accessors(chain = true) +public class BpmProcessInstanceExtDO extends BaseDO { + + /** + * 编号,自增 + */ + @TableId(type = IdType.AUTO) + private Long id; + /** + * 发起流程的用户编号 + * + * 冗余 HistoricProcessInstance 的 startUserId 属性 + */ + private Long startUserId; + /** + * 流程实例的名字 + * + * 冗余 ProcessInstance 的 name 属性,用于筛选 + */ + private String name; + /** + * 流程实例的编号 + * + * 关联 ProcessInstance 的 id 属性 + */ + private String processInstanceId; + /** + * 流程定义的编号 + * + * 关联 ProcessDefinition 的 id 属性 + */ + private String processDefinitionId; + /** + * 流程分类 + * + * 冗余 ProcessDefinition 的 category 属性 + * 数据字典 bpm_model_category + */ + private String category; + /** + * 流程实例的状态 + * + * 枚举 {@link BpmProcessInstanceStatusEnum} + */ + private Integer status; + /** + * 流程实例的结果 + * + * 枚举 {@link BpmProcessInstanceResultEnum} + */ + private Integer result; + /** + * 结束时间 + * + * 冗余 HistoricProcessInstance 的 endTime 属性 + */ + private Date endTime; + + /** + * 提交的表单值 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private Map formVariables; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/entity/task/BpmTaskExtDO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/entity/task/BpmTaskExtDO.java new file mode 100644 index 00000000..0517300f --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/entity/task/BpmTaskExtDO.java @@ -0,0 +1,88 @@ +package com.ruoyi.flowable.domain.entity.task; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.ruoyi.common.mybatis.dataobject.BaseDO; +import com.ruoyi.flowable.core.enums.task.BpmProcessInstanceResultEnum; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import lombok.experimental.Accessors; + +import java.util.Date; + +/** + * Bpm 流程任务的拓展表 + * 主要解决 Flowable Task 和 HistoricTaskInstance 不支持拓展字段,所以新建拓展表 + * + * hasPermi + */ +@TableName(value = "bpm_task_ext", autoResultMap = true) +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Accessors(chain = true) +public class BpmTaskExtDO extends BaseDO { + + /** + * 编号,自增 + */ + @TableId(type = IdType.AUTO) + private Long id; + + /** + * 任务的审批人 + * + * 冗余 Task 的 assignee 属性 + */ + private Long assigneeUserId; + /** + * 任务的名字 + * + * 冗余 Task 的 name 属性,为了筛选 + */ + private String name; + /** + * 任务的编号 + * + * 关联 Task 的 id 属性 + */ + private String taskId; +// /** +// * 任务的标识 +// * +// * 关联 {@link Task#getTaskDefinitionKey()} +// */ +// private String definitionKey; + /** + * 任务的结果 + * + * 枚举 {@link BpmProcessInstanceResultEnum} + */ + private Integer result; + /** + * 审批建议 + */ + private String reason; + /** + * 任务的结束时间 + * + * 冗余 HistoricTaskInstance 的 endTime 属性 + */ + private Date endTime; + + /** + * 流程实例的编号 + * + * 关联 ProcessInstance 的 id 属性 + */ + private String processInstanceId; + /** + * 流程定义的编号 + * + * 关联 ProcessDefinition 的 id 属性 + */ + private String processDefinitionId; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/activity/BpmActivityRespVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/activity/BpmActivityRespVO.java new file mode 100644 index 00000000..2bed8387 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/activity/BpmActivityRespVO.java @@ -0,0 +1,26 @@ +package com.ruoyi.flowable.domain.vo.activity; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.Date; + +@ApiModel("管理后台 - 流程活动的 Response VO") +@Data +public class BpmActivityRespVO { + + @ApiModelProperty(value = "流程活动的标识", required = true, example = "1024") + private String key; + @ApiModelProperty(value = "流程活动的类型", required = true, example = "StartEvent") + private String type; + + @ApiModelProperty(value = "流程活动的开始时间", required = true) + private Date startTime; + @ApiModelProperty(value = "流程活动的结束时间", required = true) + private Date endTime; + + @ApiModelProperty(value = "关联的流程任务的编号", example = "2048", notes = "关联的流程任务,只有 UserTask 等类型才有") + private String taskId; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/form/BpmFormBaseVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/form/BpmFormBaseVO.java new file mode 100644 index 00000000..1f50e8f8 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/form/BpmFormBaseVO.java @@ -0,0 +1,26 @@ +package com.ruoyi.flowable.domain.vo.form; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +/** +* 动态表单 Base VO,提供给添加、修改、详细的子 VO 使用 +* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 +*/ +@Data +public class BpmFormBaseVO { + + @ApiModelProperty(value = "表单名称", required = true, example = "芋道") + @NotNull(message = "表单名称不能为空") + private String name; + + @ApiModelProperty(value = "表单状态", required = true, notes = "参见 CommonStatusEnum 枚举", example = "1") + @NotNull(message = "表单状态不能为空") + private Integer status; + + @ApiModelProperty(value = "备注", example = "我是备注") + private String remark; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/form/BpmFormCreateReqVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/form/BpmFormCreateReqVO.java new file mode 100644 index 00000000..71207c23 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/form/BpmFormCreateReqVO.java @@ -0,0 +1,26 @@ +package com.ruoyi.flowable.domain.vo.form; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import javax.validation.constraints.NotNull; +import java.util.List; + +@ApiModel("管理后台 - 动态表单创建 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BpmFormCreateReqVO extends BpmFormBaseVO { + + @ApiModelProperty(value = "表单的配置", required = true, notes = "JSON 字符串") + @NotNull(message = "表单的配置不能为空") + private String conf; + + @ApiModelProperty(value = "表单项的数组", required = true, notes = "JSON 字符串的数组") + @NotNull(message = "表单项的数组不能为空") + private List fields; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/form/BpmFormPageReqVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/form/BpmFormPageReqVO.java new file mode 100644 index 00000000..eccfba64 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/form/BpmFormPageReqVO.java @@ -0,0 +1,19 @@ +package com.ruoyi.flowable.domain.vo.form; + +import com.ruoyi.common.mybatis.pojo.PageParam; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@ApiModel("管理后台 - 动态表单分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BpmFormPageReqVO extends PageParam { + + @ApiModelProperty(value = "表单名称", example = "芋道") + private String name; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/form/BpmFormRespVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/form/BpmFormRespVO.java new file mode 100644 index 00000000..e9b7b8e6 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/form/BpmFormRespVO.java @@ -0,0 +1,36 @@ +package com.ruoyi.flowable.domain.vo.form; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import javax.validation.constraints.NotNull; +import java.util.Date; +import java.util.List; + +@ApiModel("管理后台 - 动态表单 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BpmFormRespVO extends BpmFormBaseVO { + + @ApiModelProperty(value = "表单编号", required = true, example = "1024") + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + + @ApiModelProperty(value = "表单的配置", required = true, notes = "JSON 字符串") + @NotNull(message = "表单的配置不能为空") + private String conf; + + @ApiModelProperty(value = "表单项的数组", required = true, notes = "JSON 字符串的数组") + @NotNull(message = "表单项的数组不能为空") + private List fields; + + @ApiModelProperty(value = "创建时间", required = true) + private Date createTime; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/form/BpmFormSimpleRespVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/form/BpmFormSimpleRespVO.java new file mode 100644 index 00000000..d8adb1cc --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/form/BpmFormSimpleRespVO.java @@ -0,0 +1,17 @@ +package com.ruoyi.flowable.domain.vo.form; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@ApiModel("管理后台 - 流程表单精简 Response VO") +@Data +public class BpmFormSimpleRespVO { + + @ApiModelProperty(value = "表单编号", required = true, example = "1024") + private Long id; + + @ApiModelProperty(value = "表单名称", required = true, example = "芋道") + private String name; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/form/BpmFormUpdateReqVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/form/BpmFormUpdateReqVO.java new file mode 100644 index 00000000..1a6f6389 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/form/BpmFormUpdateReqVO.java @@ -0,0 +1,30 @@ +package com.ruoyi.flowable.domain.vo.form; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import javax.validation.constraints.NotNull; +import java.util.List; + +@ApiModel("管理后台 - 动态表单更新 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BpmFormUpdateReqVO extends BpmFormBaseVO { + + @ApiModelProperty(value = "表单编号", required = true, example = "1024") + @NotNull(message = "表单编号不能为空") + private Long id; + + @ApiModelProperty(value = "表单的配置", required = true, notes = "JSON 字符串") + @NotNull(message = "表单的配置不能为空") + private String conf; + + @ApiModelProperty(value = "表单项的数组", required = true, notes = "JSON 字符串的数组") + @NotNull(message = "表单项的数组不能为空") + private List fields; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/group/BpmUserGroupBaseVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/group/BpmUserGroupBaseVO.java new file mode 100644 index 00000000..07c75a13 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/group/BpmUserGroupBaseVO.java @@ -0,0 +1,35 @@ +package com.ruoyi.flowable.domain.vo.group; + +import javax.validation.constraints.NotNull; +import java.util.Set; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + + +/** +* 用户组 Base VO,提供给添加、修改、详细的子 VO 使用 +* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 +*/ +@Data +public class BpmUserGroupBaseVO { + + @ApiModelProperty(value = "组名", required = true, example = "芋道") + @NotNull(message = "组名不能为空") + private String name; + + @ApiModelProperty(value = "描述", required = true, example = "芋道源码") + @NotNull(message = "描述不能为空") + private String description; + + @ApiModelProperty(value = "成员编号数组", required = true, example = "1,2,3") + @NotNull(message = "成员编号数组不能为空") + private Set memberUserIds; + + @ApiModelProperty(value = "状态", required = true, example = "1") + @NotNull(message = "状态不能为空") + private Integer status; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/group/BpmUserGroupCreateReqVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/group/BpmUserGroupCreateReqVO.java new file mode 100644 index 00000000..3f3a6dc4 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/group/BpmUserGroupCreateReqVO.java @@ -0,0 +1,15 @@ +package com.ruoyi.flowable.domain.vo.group; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@ApiModel("管理后台 - 用户组创建 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BpmUserGroupCreateReqVO extends BpmUserGroupBaseVO { + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/group/BpmUserGroupPageReqVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/group/BpmUserGroupPageReqVO.java new file mode 100644 index 00000000..037f7f32 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/group/BpmUserGroupPageReqVO.java @@ -0,0 +1,29 @@ +package com.ruoyi.flowable.domain.vo.group; + +import com.ruoyi.common.mybatis.pojo.PageParam; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +@ApiModel("管理后台 - 用户组分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BpmUserGroupPageReqVO extends PageParam { + + @ApiModelProperty(value = "组名", example = "芋道") + private String name; + + @ApiModelProperty(value = "状态", example = "1") + private Integer status; + + @DateTimeFormat(pattern ="yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "创建时间") + private Date[] createTime; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/group/BpmUserGroupRespVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/group/BpmUserGroupRespVO.java new file mode 100644 index 00000000..fdcab9a2 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/group/BpmUserGroupRespVO.java @@ -0,0 +1,22 @@ +package com.ruoyi.flowable.domain.vo.group; + +import java.util.Date; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@ApiModel("管理后台 - 用户组 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BpmUserGroupRespVO extends BpmUserGroupBaseVO { + + @ApiModelProperty(value = "编号", required = true, example = "1024") + private Long id; + + @ApiModelProperty(value = "创建时间", required = true) + private Date createTime; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/group/BpmUserGroupSimpleRespVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/group/BpmUserGroupSimpleRespVO.java new file mode 100644 index 00000000..89a7f104 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/group/BpmUserGroupSimpleRespVO.java @@ -0,0 +1,21 @@ +package com.ruoyi.flowable.domain.vo.group; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@ApiModel("管理后台 - 用户组精简信息 Response VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class BpmUserGroupSimpleRespVO { + + @ApiModelProperty(value = "用户组编号", required = true, example = "1024") + private Long id; + + @ApiModelProperty(value = "用户组名字", required = true, example = "芋道") + private String name; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/group/BpmUserGroupUpdateReqVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/group/BpmUserGroupUpdateReqVO.java new file mode 100644 index 00000000..2b0e947b --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/group/BpmUserGroupUpdateReqVO.java @@ -0,0 +1,20 @@ +package com.ruoyi.flowable.domain.vo.group; + +import javax.validation.constraints.NotNull; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@ApiModel("管理后台 - 用户组更新 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BpmUserGroupUpdateReqVO extends BpmUserGroupBaseVO { + + @ApiModelProperty(value = "编号", required = true, example = "1024") + @NotNull(message = "编号不能为空") + private Long id; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstanceCancelReqVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstanceCancelReqVO.java new file mode 100644 index 00000000..e7147dfe --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstanceCancelReqVO.java @@ -0,0 +1,21 @@ +package com.ruoyi.flowable.domain.vo.instance; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; + +@ApiModel("管理后台 - 流程实例的取消 Request VO") +@Data +public class BpmProcessInstanceCancelReqVO { + + @ApiModelProperty(value = "流程实例的编号", required = true, example = "1024") + @NotEmpty(message = "流程实例的编号不能为空") + private String id; + + @ApiModelProperty(value = "取消原因", required = true, example = "不请假了!") + @NotEmpty(message = "取消原因不能为空") + private String reason; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstanceCreateReqVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstanceCreateReqVO.java new file mode 100644 index 00000000..c6da8e2a --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstanceCreateReqVO.java @@ -0,0 +1,21 @@ +package com.ruoyi.flowable.domain.vo.instance; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; +import java.util.Map; + +@ApiModel("管理后台 - 流程实例的创建 Request VO") +@Data +public class BpmProcessInstanceCreateReqVO { + + @ApiModelProperty(value = "流程定义的编号", required = true, example = "1024") + @NotEmpty(message = "流程定义编号不能为空") + private String processDefinitionId; + + @ApiModelProperty(value = "变量实例") + private Map variables; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstanceMyPageReqVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstanceMyPageReqVO.java new file mode 100644 index 00000000..e1e393df --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstanceMyPageReqVO.java @@ -0,0 +1,39 @@ +package com.ruoyi.flowable.domain.vo.instance; + +import com.ruoyi.common.mybatis.pojo.PageParam; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + + +@ApiModel("管理后台 - 流程实例的分页 Item Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BpmProcessInstanceMyPageReqVO extends PageParam { + + @ApiModelProperty(value = "流程名称", example = "芋道") + private String name; + + @ApiModelProperty(value = "流程定义的编号", example = "2048") + private String processDefinitionId; + + @ApiModelProperty(value = "流程实例的状态", notes = "参见 bpm_process_instance_status", example = "1") + private Integer status; + + @ApiModelProperty(value = "流程实例的结果", notes = "参见 bpm_process_instance_result", example = "2") + private Integer result; + + @ApiModelProperty(value = "流程分类", notes = "参见 bpm_model_category 数据字典", example = "1") + private String category; + + @ApiModelProperty(value = "创建时间") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date[] createTime; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstancePageItemRespVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstancePageItemRespVO.java new file mode 100644 index 00000000..ff40fa13 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstancePageItemRespVO.java @@ -0,0 +1,55 @@ +package com.ruoyi.flowable.domain.vo.instance; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.Date; +import java.util.List; + +@ApiModel("管理后台 - 流程实例的分页 Item Response VO") +@Data +public class BpmProcessInstancePageItemRespVO { + + @ApiModelProperty(value = "流程实例的编号", required = true, example = "1024") + private String id; + + @ApiModelProperty(value = "流程名称", required = true, example = "芋道") + private String name; + + @ApiModelProperty(value = "流程定义的编号", required = true, example = "2048") + private String processDefinitionId; + + @ApiModelProperty(value = "流程分类", required = true, notes = "参见 bpm_model_category 数据字典", example = "1") + private String category; + + @ApiModelProperty(value = "流程实例的状态", required = true, notes = "参见 bpm_process_instance_status", example = "1") + private Integer status; + + @ApiModelProperty(value = "流程实例的结果", required = true, notes = "参见 bpm_process_instance_result", example = "2") + private Integer result; + + @ApiModelProperty(value = "提交时间", required = true) + private Date createTime; + + @ApiModelProperty(value = "结束时间", required = true) + private Date endTime; + + /** + * 当前任务 + */ + private List tasks; + + @ApiModel("流程任务") + @Data + public static class Task { + + @ApiModelProperty(value = "流程任务的编号", required = true, example = "1024") + private String id; + + @ApiModelProperty(value = "任务名称", required = true, example = "芋道") + private String name; + + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstanceRespVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstanceRespVO.java new file mode 100644 index 00000000..d122d777 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstanceRespVO.java @@ -0,0 +1,97 @@ +package com.ruoyi.flowable.domain.vo.instance; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +@ApiModel("管理后台 - 流程实例的 Response VO") +@Data +public class BpmProcessInstanceRespVO { + + @ApiModelProperty(value = "流程实例的编号", required = true, example = "1024") + private String id; + + @ApiModelProperty(value = "流程名称", required = true, example = "芋道") + private String name; + + @ApiModelProperty(value = "流程分类", required = true, notes = "参见 bpm_model_category 数据字典", example = "1") + private String category; + + @ApiModelProperty(value = "流程实例的状态", required = true, notes = "参见 bpm_process_instance_status", example = "1") + private Integer status; + + @ApiModelProperty(value = "流程实例的结果", required = true, notes = "参见 bpm_process_instance_result", example = "2") + private Integer result; + + @ApiModelProperty(value = "提交时间", required = true) + private Date createTime; + + @ApiModelProperty(value = "结束时间", required = true) + private Date endTime; + + @ApiModelProperty(value = "提交的表单值", required = true) + private Map formVariables; + + @ApiModelProperty(value = "业务的唯一标识", example = "1", notes = "例如说,请假申请的编号") + private String businessKey; + + /** + * 发起流程的用户 + */ + private User startUser; + + /** + * 流程定义 + */ + private ProcessDefinition processDefinition; + + @ApiModel("用户信息") + @Data + public static class User { + + @ApiModelProperty(value = "用户编号", required = true, example = "1") + private Long id; + @ApiModelProperty(value = "用户昵称", required = true, example = "芋艿") + private String nickname; + + @ApiModelProperty(value = "部门编号", required = true, example = "1") + private Long deptId; + @ApiModelProperty(value = "部门名称", required = true, example = "研发部") + private String deptName; + + } + + @ApiModel("流程定义信息") + @Data + public static class ProcessDefinition { + + @ApiModelProperty(value = "编号", required = true, example = "1024") + private String id; + + @ApiModelProperty(value = "表单类型", notes = "参见 bpm_model_form_type 数据字典", example = "1") + private Integer formType; + @ApiModelProperty(value = "表单编号", example = "1024", notes = "在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空") + private Long formId; + @ApiModelProperty(value = "表单的配置", required = true, + notes = "JSON 字符串。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空") + private String formConf; + @ApiModelProperty(value = "表单项的数组", required = true, + notes = "JSON 字符串的数组。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空") + private List formFields; + @ApiModelProperty(value = "自定义表单的提交路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/create", + notes = "在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空") + private String formCustomCreatePath; + @ApiModelProperty(value = "自定义表单的查看路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/view", + notes = "在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空") + private String formCustomViewPath; + + @ApiModelProperty(value = "BPMN XML", required = true) + private String bpmnXml; + + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/model/BpmModeImportReqVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/model/BpmModeImportReqVO.java new file mode 100644 index 00000000..4139f027 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/model/BpmModeImportReqVO.java @@ -0,0 +1,22 @@ +package com.ruoyi.flowable.domain.vo.model; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.web.multipart.MultipartFile; + +import javax.validation.constraints.NotNull; + +@ApiModel(value = "管理后台 - 流程模型的导入 Request VO", description = "相比流程模型的新建来说,只是多了一个 bpmnFile 文件") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BpmModeImportReqVO extends BpmModelCreateReqVO { + + @ApiModelProperty(value = "BPMN 文件", required = true) + @NotNull(message = "BPMN 文件不能为空") + private MultipartFile bpmnFile; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/model/BpmModelBaseVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/model/BpmModelBaseVO.java new file mode 100644 index 00000000..cf7ce48e --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/model/BpmModelBaseVO.java @@ -0,0 +1,41 @@ +package com.ruoyi.flowable.domain.vo.model; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; + +/** +* 流程模型 Base VO,提供给添加、修改、详细的子 VO 使用 +* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 +*/ +@Data +public class BpmModelBaseVO { + + @ApiModelProperty(value = "流程标识", required = true, example = "process_yudao") + @NotEmpty(message = "流程标识不能为空") + private String key; + + @ApiModelProperty(value = "流程名称", required = true, example = "芋道") + @NotEmpty(message = "流程名称不能为空") + private String name; + + @ApiModelProperty(value = "流程描述", example = "我是描述") + private String description; + + @ApiModelProperty(value = "流程分类", notes = "参见 bpm_model_category 数据字典", example = "1") + @NotEmpty(message = "流程分类不能为空") + private String category; + + @ApiModelProperty(value = "表单类型", notes = "参见 bpm_model_form_type 数据字典", example = "1") + private Integer formType; + @ApiModelProperty(value = "表单编号", example = "1024", notes = "在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空") + private Long formId; + @ApiModelProperty(value = "自定义表单的提交路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/create", + notes = "在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空") + private String formCustomCreatePath; + @ApiModelProperty(value = "自定义表单的查看路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/view", + notes = "在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空") + private String formCustomViewPath; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/model/BpmModelCreateReqVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/model/BpmModelCreateReqVO.java new file mode 100644 index 00000000..25d1de52 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/model/BpmModelCreateReqVO.java @@ -0,0 +1,24 @@ +package com.ruoyi.flowable.domain.vo.model; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; + +@ApiModel("管理后台 - 流程模型的创建 Request VO") +@Data +public class BpmModelCreateReqVO { + + @ApiModelProperty(value = "流程标识", required = true, example = "process_yudao") + @NotEmpty(message = "流程标识不能为空") + private String key; + + @ApiModelProperty(value = "流程名称", required = true, example = "芋道") + @NotEmpty(message = "流程名称不能为空") + private String name; + + @ApiModelProperty(value = "流程描述", example = "我是描述") + private String description; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/model/BpmModelPageItemRespVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/model/BpmModelPageItemRespVO.java new file mode 100644 index 00000000..accc6a28 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/model/BpmModelPageItemRespVO.java @@ -0,0 +1,49 @@ +package com.ruoyi.flowable.domain.vo.model; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.Date; + +@ApiModel("管理后台 - 流程模型的分页的每一项 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BpmModelPageItemRespVO extends BpmModelBaseVO { + + @ApiModelProperty(value = "编号", required = true, example = "1024") + private String id; + + @ApiModelProperty(value = "表单名字", example = "请假表单") + private String formName; + + @ApiModelProperty(value = "创建时间", required = true) + private Date createTime; + + /** + * 最新部署的流程定义 + */ + private ProcessDefinition processDefinition; + + @ApiModel("流程定义") + @Data + public static class ProcessDefinition { + + @ApiModelProperty(value = "编号", required = true, example = "1024") + private String id; + + @ApiModelProperty(value = "版本", required = true, example = "1") + private Integer version; + + @ApiModelProperty(value = "部署时间", required = true) + private Date deploymentTime; + + @ApiModelProperty(value = "中断状态", required = true, example = "1", notes = "参见 SuspensionState 枚举") + private Integer suspensionState; + + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/model/BpmModelPageReqVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/model/BpmModelPageReqVO.java new file mode 100644 index 00000000..6db07bcb --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/model/BpmModelPageReqVO.java @@ -0,0 +1,26 @@ +package com.ruoyi.flowable.domain.vo.model; + +import com.ruoyi.common.mybatis.pojo.PageParam; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + + +@ApiModel("管理后台 - 流程模型分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BpmModelPageReqVO extends PageParam { + + @ApiModelProperty(value = "标识", example = "process1641042089407", notes = "精准匹配") + private String key; + + @ApiModelProperty(value = "名字", example = "芋道", notes = "模糊匹配") + private String name; + + @ApiModelProperty(value = "流程分类", notes = "参见 bpm_model_category 数据字典", example = "1") + private String category; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/model/BpmModelRespVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/model/BpmModelRespVO.java new file mode 100644 index 00000000..7a5d0ae6 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/model/BpmModelRespVO.java @@ -0,0 +1,26 @@ +package com.ruoyi.flowable.domain.vo.model; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.Date; + +@ApiModel("管理后台 - 流程模型的创建 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BpmModelRespVO extends BpmModelBaseVO { + + @ApiModelProperty(value = "编号", required = true, example = "1024") + private String id; + + @ApiModelProperty(value = "BPMN XML", required = true) + private String bpmnXml; + + @ApiModelProperty(value = "创建时间", required = true) + private Date createTime; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/model/BpmModelUpdateReqVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/model/BpmModelUpdateReqVO.java new file mode 100644 index 00000000..c4b6fb2a --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/model/BpmModelUpdateReqVO.java @@ -0,0 +1,40 @@ +package com.ruoyi.flowable.domain.vo.model; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; + +@ApiModel("管理后台 - 流程模型的更新 Request VO") +@Data +public class BpmModelUpdateReqVO { + + @ApiModelProperty(value = "编号", required = true, example = "1024") + @NotEmpty(message = "编号不能为空") + private String id; + + @ApiModelProperty(value = "流程名称", example = "芋道") + private String name; + + @ApiModelProperty(value = "流程描述", example = "我是描述") + private String description; + + @ApiModelProperty(value = "流程分类", notes = "参见 bpm_model_category 数据字典", example = "1") + private String category; + + @ApiModelProperty(value = "BPMN XML", required = true) + private String bpmnXml; + + @ApiModelProperty(value = "表单类型", notes = "参见 bpm_model_form_type 数据字典", example = "1") + private Integer formType; + @ApiModelProperty(value = "表单编号", example = "1024", notes = "在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空") + private Long formId; + @ApiModelProperty(value = "自定义表单的提交路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/create", + notes = "在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空") + private String formCustomCreatePath; + @ApiModelProperty(value = "自定义表单的查看路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/view", + notes = "在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空") + private String formCustomViewPath; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/model/BpmModelUpdateStateReqVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/model/BpmModelUpdateStateReqVO.java new file mode 100644 index 00000000..e3e77bf2 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/model/BpmModelUpdateStateReqVO.java @@ -0,0 +1,21 @@ +package com.ruoyi.flowable.domain.vo.model; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +@ApiModel("管理后台 - 流程模型更新状态 Request VO") +@Data +public class BpmModelUpdateStateReqVO { + + @ApiModelProperty(value = "编号", required = true, example = "1024") + @NotNull(message = "编号不能为空") + private String id; + + @ApiModelProperty(value = "状态", required = true, example = "1", notes = "见 SuspensionState 枚举") + @NotNull(message = "状态不能为空") + private Integer state; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/oa/BpmOALeaveBaseVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/oa/BpmOALeaveBaseVO.java new file mode 100644 index 00000000..45eba61b --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/oa/BpmOALeaveBaseVO.java @@ -0,0 +1,33 @@ +package com.ruoyi.flowable.domain.vo.oa; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.validation.constraints.NotNull; +import java.util.Date; + + +/** +* 请假申请 Base VO,提供给添加、修改、详细的子 VO 使用 +* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 +*/ +@Data +public class BpmOALeaveBaseVO { + + @ApiModelProperty(value = "请假的开始时间", required = true) + @NotNull(message = "开始时间不能为空") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date startTime; + @ApiModelProperty(value = "请假的结束时间", required = true) + @NotNull(message = "结束时间不能为空") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date endTime; + + @ApiModelProperty(value = "请假类型", required = true, example = "1", notes = "参见 bpm_oa_type 枚举") + private Integer type; + + @ApiModelProperty(value = "原因", required = true, example = "阅读芋道源码") + private String reason; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/oa/BpmOALeaveCreateReqVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/oa/BpmOALeaveCreateReqVO.java new file mode 100644 index 00000000..ea9bb572 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/oa/BpmOALeaveCreateReqVO.java @@ -0,0 +1,21 @@ +package com.ruoyi.flowable.domain.vo.oa; + +import io.swagger.annotations.ApiModel; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import javax.validation.constraints.AssertTrue; + +@ApiModel("管理后台 - 请假申请创建 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BpmOALeaveCreateReqVO extends BpmOALeaveBaseVO { + + @AssertTrue(message = "结束时间,需要在开始时间之后") + public boolean isEndTimeValid() { + return !getEndTime().before(getStartTime()); + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/oa/BpmOALeavePageReqVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/oa/BpmOALeavePageReqVO.java new file mode 100644 index 00000000..98adaf67 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/oa/BpmOALeavePageReqVO.java @@ -0,0 +1,33 @@ +package com.ruoyi.flowable.domain.vo.oa; + +import com.ruoyi.common.mybatis.pojo.PageParam; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + + +@ApiModel("管理后台 - 请假申请分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BpmOALeavePageReqVO extends PageParam { + + @ApiModelProperty(value = "状态", example = "1", notes = "参见 bpm_process_instance_result 枚举") + private Integer result; + + @ApiModelProperty(value = "请假类型", example = "1", notes = "参见 bpm_oa_type") + private Integer type; + + @ApiModelProperty(value = "原因", example = "阅读芋道源码", notes = "模糊匹配") + private String reason; + + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "申请时间") + private Date[] createTime; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/oa/BpmOALeaveRespVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/oa/BpmOALeaveRespVO.java new file mode 100644 index 00000000..c2d98bd3 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/oa/BpmOALeaveRespVO.java @@ -0,0 +1,33 @@ +package com.ruoyi.flowable.domain.vo.oa; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.validation.constraints.NotNull; +import java.util.Date; + +@ApiModel("管理后台 - 请假申请 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BpmOALeaveRespVO extends BpmOALeaveBaseVO { + + @ApiModelProperty(value = "请假表单主键", required = true, example = "1024") + private Long id; + + @ApiModelProperty(value = "状态", required = true, example = "1", notes = "参见 bpm_process_instance_result 枚举") + private Integer result; + + @ApiModelProperty(value = "申请时间", required = true) + @NotNull(message = "申请时间不能为空") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @ApiModelProperty(value = "流程id") + private String processInstanceId; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/process/BpmProcessDefinitionListReqVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/process/BpmProcessDefinitionListReqVO.java new file mode 100644 index 00000000..debd90cb --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/process/BpmProcessDefinitionListReqVO.java @@ -0,0 +1,19 @@ +package com.ruoyi.flowable.domain.vo.process; + +import com.ruoyi.common.mybatis.pojo.PageParam; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@ApiModel("管理后台 - 流程定义列表 Request VO") +@Data +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = true) +public class BpmProcessDefinitionListReqVO extends PageParam { + + @ApiModelProperty(value = "中断状态", example = "1", notes = "参见 SuspensionState 枚举") + private Integer suspensionState; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/process/BpmProcessDefinitionPageItemRespVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/process/BpmProcessDefinitionPageItemRespVO.java new file mode 100644 index 00000000..8e685872 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/process/BpmProcessDefinitionPageItemRespVO.java @@ -0,0 +1,23 @@ +package com.ruoyi.flowable.domain.vo.process; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.Date; + +@ApiModel("管理后台 - 流程定义的分页的每一项 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BpmProcessDefinitionPageItemRespVO extends BpmProcessDefinitionRespVO { + + @ApiModelProperty(value = "表单名字", example = "请假表单") + private String formName; + + @ApiModelProperty(value = "部署时间", required = true) + private Date deploymentTime; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/process/BpmProcessDefinitionPageReqVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/process/BpmProcessDefinitionPageReqVO.java new file mode 100644 index 00000000..47bab053 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/process/BpmProcessDefinitionPageReqVO.java @@ -0,0 +1,19 @@ +package com.ruoyi.flowable.domain.vo.process; + +import com.ruoyi.common.mybatis.pojo.PageParam; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@ApiModel("管理后台 - 流程定义分页 Request VO") +@Data +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = true) +public class BpmProcessDefinitionPageReqVO extends PageParam { + + @ApiModelProperty(value = "标识", example = "process1641042089407", notes = "精准匹配") + private String key; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/process/BpmProcessDefinitionRespVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/process/BpmProcessDefinitionRespVO.java new file mode 100644 index 00000000..544854e7 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/process/BpmProcessDefinitionRespVO.java @@ -0,0 +1,51 @@ +package com.ruoyi.flowable.domain.vo.process; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; +import java.util.List; + +@ApiModel("管理后台 - 流程定义 Response VO") +@Data +public class BpmProcessDefinitionRespVO { + + @ApiModelProperty(value = "编号", required = true, example = "1024") + private String id; + + @ApiModelProperty(value = "版本", required = true, example = "1") + private Integer version; + + @ApiModelProperty(value = "流程名称", required = true, example = "芋道") + @NotEmpty(message = "流程名称不能为空") + private String name; + + @ApiModelProperty(value = "流程描述", example = "我是描述") + private String description; + + @ApiModelProperty(value = "流程分类", notes = "参见 bpm_model_category 数据字典", example = "1") + @NotEmpty(message = "流程分类不能为空") + private String category; + + @ApiModelProperty(value = "表单类型", notes = "参见 bpm_model_form_type 数据字典", example = "1") + private Integer formType; + @ApiModelProperty(value = "表单编号", example = "1024", notes = "在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空") + private Long formId; + @ApiModelProperty(value = "表单的配置", required = true, + notes = "JSON 字符串。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空") + private String formConf; + @ApiModelProperty(value = "表单项的数组", required = true, + notes = "JSON 字符串的数组。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空") + private List formFields; + @ApiModelProperty(value = "自定义表单的提交路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/create", + notes = "在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空") + private String formCustomCreatePath; + @ApiModelProperty(value = "自定义表单的查看路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/view", + notes = "在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空") + private String formCustomViewPath; + + @ApiModelProperty(value = "中断状态", required = true, example = "1", notes = "参见 SuspensionState 枚举") + private Integer suspensionState; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/rule/BpmTaskAssignRuleBaseVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/rule/BpmTaskAssignRuleBaseVO.java new file mode 100644 index 00000000..73c91824 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/rule/BpmTaskAssignRuleBaseVO.java @@ -0,0 +1,24 @@ +package com.ruoyi.flowable.domain.vo.rule; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotNull; +import java.util.Set; + +/** + * 流程任务分配规则 Base VO,提供给添加、修改、详细的子 VO 使用 + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 + */ +@Data +public class BpmTaskAssignRuleBaseVO { + + @ApiModelProperty(value = "规则类型", required = true, example = "bpm_task_assign_rule_type") + @NotNull(message = "规则类型不能为空") + private Integer type; + + @ApiModelProperty(value = "规则值数组", required = true, example = "1,2,3") + @NotNull(message = "规则值数组不能为空") + private Set options; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/rule/BpmTaskAssignRuleCreateReqVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/rule/BpmTaskAssignRuleCreateReqVO.java new file mode 100644 index 00000000..45a16421 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/rule/BpmTaskAssignRuleCreateReqVO.java @@ -0,0 +1,25 @@ +package com.ruoyi.flowable.domain.vo.rule; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import javax.validation.constraints.NotEmpty; + +@ApiModel("管理后台 - 流程任务分配规则的创建 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BpmTaskAssignRuleCreateReqVO extends BpmTaskAssignRuleBaseVO { + + @ApiModelProperty(value = "流程模型的编号", required = true, example = "1024") + @NotEmpty(message = "流程模型的编号不能为空") + private String modelId; + + @ApiModelProperty(value = "流程任务定义的编号", required = true, example = "2048") + @NotEmpty(message = "流程任务定义的编号不能为空") + private String taskDefinitionKey; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/rule/BpmTaskAssignRuleRespVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/rule/BpmTaskAssignRuleRespVO.java new file mode 100644 index 00000000..dd43e126 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/rule/BpmTaskAssignRuleRespVO.java @@ -0,0 +1,29 @@ +package com.ruoyi.flowable.domain.vo.rule; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@ApiModel("管理后台 - 流程任务分配规则的 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BpmTaskAssignRuleRespVO extends BpmTaskAssignRuleBaseVO { + + @ApiModelProperty(value = "任务分配规则的编号", required = true, example = "1024") + private Long id; + + @ApiModelProperty(value = "流程模型的编号", required = true, example = "2048") + private String modelId; + + @ApiModelProperty(value = "流程定义的编号", required = true, example = "4096") + private String processDefinitionId; + + @ApiModelProperty(value = "流程任务定义的编号", required = true, example = "2048") + private String taskDefinitionKey; + @ApiModelProperty(value = "流程任务定义的名字", required = true, example = "关注芋道") + private String taskDefinitionName; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/rule/BpmTaskAssignRuleUpdateReqVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/rule/BpmTaskAssignRuleUpdateReqVO.java new file mode 100644 index 00000000..1feae03f --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/rule/BpmTaskAssignRuleUpdateReqVO.java @@ -0,0 +1,21 @@ +package com.ruoyi.flowable.domain.vo.rule; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import javax.validation.constraints.NotNull; + +@ApiModel("管理后台 - 流程任务分配规则的更新 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BpmTaskAssignRuleUpdateReqVO extends BpmTaskAssignRuleBaseVO { + + @ApiModelProperty(value = "任务分配规则的编号", required = true, example = "1024") + @NotNull(message = "任务分配规则的编号不能为空") + private Long id; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/task/BackTaskVo.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/task/BackTaskVo.java new file mode 100644 index 00000000..81ddcb93 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/task/BackTaskVo.java @@ -0,0 +1,42 @@ +package com.ruoyi.flowable.domain.vo.task; + + +import lombok.Data; + +/** + * @author wangzongrun + */ +@Data +public class BackTaskVo { + + /** + * 需要驳回的节点id 必填 + */ + private String distFlowElementId; + + /** + * 操作人code 必填 + */ + private String userCode; + + /** + * 任务id 必填 + */ + private String taskId; + + /** + * 流程实例的id 必填 + */ + private String processInstanceId; + + /** + * 审批意见 必填 + */ + private String message; + + /** + * 审批类型 必填 + */ + private String type; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/task/BpmTaskApproveReqVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/task/BpmTaskApproveReqVO.java new file mode 100644 index 00000000..7e013dd5 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/task/BpmTaskApproveReqVO.java @@ -0,0 +1,21 @@ +package com.ruoyi.flowable.domain.vo.task; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; + +@ApiModel("管理后台 - 通过流程任务的 Request VO") +@Data +public class BpmTaskApproveReqVO { + + @ApiModelProperty(value = "任务编号", required = true, example = "1024") + @NotEmpty(message = "任务编号不能为空") + private String id; + + @ApiModelProperty(value = "审批意见", required = true, example = "不错不错!") + @NotEmpty(message = "审批意见不能为空") + private String reason; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/task/BpmTaskDonePageItemRespVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/task/BpmTaskDonePageItemRespVO.java new file mode 100644 index 00000000..c96fa73b --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/task/BpmTaskDonePageItemRespVO.java @@ -0,0 +1,27 @@ +package com.ruoyi.flowable.domain.vo.task; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.Date; + +@ApiModel("管理后台 - 流程任务的 Done 已完成的分页项 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BpmTaskDonePageItemRespVO extends BpmTaskTodoPageItemRespVO { + + @ApiModelProperty(value = "结束时间", required = true) + private Date endTime; + @ApiModelProperty(value = "持续时间", required = true, example = "1000") + private Long durationInMillis; + + @ApiModelProperty(value = "任务结果", required = true, notes = "参见 bpm_process_instance_result", example = "2") + private Integer result; + @ApiModelProperty(value = "审批建议", required = true, example = "不请假了!") + private String reason; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/task/BpmTaskDonePageReqVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/task/BpmTaskDonePageReqVO.java new file mode 100644 index 00000000..912cad1e --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/task/BpmTaskDonePageReqVO.java @@ -0,0 +1,31 @@ +package com.ruoyi.flowable.domain.vo.task; + +import com.ruoyi.common.mybatis.pojo.PageParam; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + + +@ApiModel("管理后台 - 流程任务的 Done 已办的分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BpmTaskDonePageReqVO extends PageParam { + + @ApiModelProperty(value = "流程任务名", example = "芋道") + private String name; + + @ApiModelProperty(value = "开始的创建收间") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date beginCreateTime; + + @ApiModelProperty(value = "结束的创建时间") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date endCreateTime; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/task/BpmTaskRejectReqVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/task/BpmTaskRejectReqVO.java new file mode 100644 index 00000000..7770f038 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/task/BpmTaskRejectReqVO.java @@ -0,0 +1,21 @@ +package com.ruoyi.flowable.domain.vo.task; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; + +@ApiModel("管理后台 - 不通过流程任务的 Request VO") +@Data +public class BpmTaskRejectReqVO { + + @ApiModelProperty(value = "任务编号", required = true, example = "1024") + @NotEmpty(message = "任务编号不能为空") + private String id; + + @ApiModelProperty(value = "审批意见", required = true, example = "不错不错!") + @NotEmpty(message = "审批意见不能为空") + private String reason; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/task/BpmTaskRespVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/task/BpmTaskRespVO.java new file mode 100644 index 00000000..5851b57a --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/task/BpmTaskRespVO.java @@ -0,0 +1,38 @@ +package com.ruoyi.flowable.domain.vo.task; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@ApiModel("管理后台 - 流程任务的 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BpmTaskRespVO extends BpmTaskDonePageItemRespVO { + + @ApiModelProperty(value = "任务定义的标识", required = true, example = "user-001") + private String definitionKey; + + /** + * 审核的用户信息 + */ + private User assigneeUser; + + @ApiModel("用户信息") + @Data + public static class User { + + @ApiModelProperty(value = "用户编号", required = true, example = "1") + private Long id; + @ApiModelProperty(value = "用户昵称", required = true, example = "芋艿") + private String nickname; + + @ApiModelProperty(value = "部门编号", required = true, example = "1") + private Long deptId; + @ApiModelProperty(value = "部门名称", required = true, example = "研发部") + private String deptName; + + } +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/task/BpmTaskTodoPageItemRespVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/task/BpmTaskTodoPageItemRespVO.java new file mode 100644 index 00000000..d269b0bb --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/task/BpmTaskTodoPageItemRespVO.java @@ -0,0 +1,61 @@ +package com.ruoyi.flowable.domain.vo.task; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.Date; + +@ApiModel("管理后台 - 流程任务的 Running 进行中的分页项 Response VO") +@Data +public class BpmTaskTodoPageItemRespVO { + + @ApiModelProperty(value = "任务编号", required = true, example = "1024") + private String id; + + @ApiModelProperty(value = "任务名字", required = true, example = "芋道") + private String name; + + @ApiModelProperty(value = "接收时间", required = true) + private Date claimTime; + + @ApiModelProperty(value = "创建时间", required = true) + private Date createTime; + + @ApiModelProperty(value = "激活状态", required = true, example = "1", notes = "参见 SuspensionState 枚举") + private Integer suspensionState; + + @ApiModelProperty(value = "任务定义的标识") + private String taskDefinitionKey; + + /** + * 所属流程实例 + */ + private ProcessInstance processInstance; + + @Data + @ApiModel("流程实例") + public static class ProcessInstance { + + @ApiModelProperty(value = "流程实例编号", required = true, example = "1024") + private String id; + + @ApiModelProperty(value = "流程实例名称", required = true, example = "芋道") + private String name; + + @ApiModelProperty(value = "发起人的用户编号", required = true, example = "1024") + private Long startUserId; + + @ApiModelProperty(value = "发起人的用户昵称", required = true, example = "芋艿") + private String startUserNickname; + + @ApiModelProperty(value = "流程定义的编号", required = true, example = "2048") + private String processDefinitionId; + + @ApiModelProperty(value = "业务的唯一标识") + private String businessKey; + @ApiModelProperty(value = "流程定义标识") + private String processDefinitionKey; + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/task/BpmTaskTodoPageReqVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/task/BpmTaskTodoPageReqVO.java new file mode 100644 index 00000000..7546889e --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/task/BpmTaskTodoPageReqVO.java @@ -0,0 +1,31 @@ +package com.ruoyi.flowable.domain.vo.task; + +import com.ruoyi.common.mybatis.pojo.PageParam; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + + +@ApiModel("管理后台 - 流程任务的 TODO 待办的分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BpmTaskTodoPageReqVO extends PageParam { + + @ApiModelProperty(value = "流程任务名", example = "芋道") + private String name; + + @ApiModelProperty(value = "开始的创建收间") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date beginCreateTime; + + @ApiModelProperty(value = "结束的创建时间") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date endCreateTime; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/task/BpmTaskUpdateAssigneeReqVO.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/task/BpmTaskUpdateAssigneeReqVO.java new file mode 100644 index 00000000..9294ee3c --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/task/BpmTaskUpdateAssigneeReqVO.java @@ -0,0 +1,22 @@ +package com.ruoyi.flowable.domain.vo.task; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +@ApiModel("管理后台 - 流程任务的更新负责人的 Request VO") +@Data +public class BpmTaskUpdateAssigneeReqVO { + + @ApiModelProperty(value = "任务编号", required = true, example = "1024") + @NotEmpty(message = "任务编号不能为空") + private String id; + + @ApiModelProperty(value = "新审批人的用户编号", required = true, example = "2048") + @NotNull(message = "新审批人的用户编号不能为空") + private Long assigneeUserId; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/task/FlowNodeVo.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/task/FlowNodeVo.java new file mode 100644 index 00000000..11152c8f --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/vo/task/FlowNodeVo.java @@ -0,0 +1,33 @@ +package com.ruoyi.flowable.domain.vo.task; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * + * @author wangzongrun + */ +@ApiModel("流程节点对象") +@Data +public class FlowNodeVo implements Serializable { + + @ApiModelProperty(value = "节点id") + private String nodeId; + + @ApiModelProperty(value = "节点名称") + private String nodeName; + + @ApiModelProperty(value = "执行人的code") + private String userCode; + + @ApiModelProperty(value = "执行人姓名") + private String userName; + + @ApiModelProperty(value = "任务节点结束时间") + private Date endTime; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/bpm/config/BpmCommonConfiguration.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/bpm/config/BpmCommonConfiguration.java new file mode 100644 index 00000000..ff709354 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/bpm/config/BpmCommonConfiguration.java @@ -0,0 +1,19 @@ +package com.ruoyi.flowable.framework.bpm.config; + +import com.ruoyi.flowable.framework.bpm.core.event.BpmProcessInstanceResultEventPublisher; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * BPM 通用的 Configuration 配置类,提供给 Activiti 和 Flowable + */ +@Configuration +public class BpmCommonConfiguration { + + @Bean + public BpmProcessInstanceResultEventPublisher processInstanceResultEventPublisher(ApplicationEventPublisher publisher) { + return new BpmProcessInstanceResultEventPublisher(publisher); + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/bpm/core/event/BpmProcessInstanceResultEvent.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/bpm/core/event/BpmProcessInstanceResultEvent.java new file mode 100644 index 00000000..a8b6c37f --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/bpm/core/event/BpmProcessInstanceResultEvent.java @@ -0,0 +1,43 @@ +package com.ruoyi.flowable.framework.bpm.core.event; + +import lombok.Data; +import org.springframework.context.ApplicationEvent; + +import javax.validation.constraints.NotNull; + +/** + * 流程实例的结果发生变化的 Event + * 定位:由于额外增加了 {@link BpmProcessInstanceExtDO#getResult()} 结果,所以增加该事件 + * + * hasPermi + */ +@SuppressWarnings("ALL") +@Data +public class BpmProcessInstanceResultEvent extends ApplicationEvent { + + /** + * 流程实例的编号 + */ + @NotNull(message = "流程实例的编号不能为空") + private String id; + /** + * 流程实例的 key + */ + @NotNull(message = "流程实例的 key 不能为空") + private String processDefinitionKey; + /** + * 流程实例的结果 + */ + @NotNull(message = "流程实例的结果不能为空") + private Integer result; + /** + * 流程实例对应的业务标识 + * 例如说,请假 + */ + private String businessKey; + + public BpmProcessInstanceResultEvent(Object source) { + super(source); + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/bpm/core/event/BpmProcessInstanceResultEventListener.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/bpm/core/event/BpmProcessInstanceResultEventListener.java new file mode 100644 index 00000000..89da1355 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/bpm/core/event/BpmProcessInstanceResultEventListener.java @@ -0,0 +1,34 @@ +package com.ruoyi.flowable.framework.bpm.core.event; + +import cn.hutool.core.util.StrUtil; +import org.springframework.context.ApplicationListener; + +/** + * {@link BpmProcessInstanceResultEvent} 的监听器 + * + * hasPermi + */ +public abstract class BpmProcessInstanceResultEventListener + implements ApplicationListener { + + @Override + public final void onApplicationEvent(BpmProcessInstanceResultEvent event) { + if (!StrUtil.equals(event.getProcessDefinitionKey(), getProcessDefinitionKey())) { + return; + } + onEvent(event); + } + + /** + * @return 返回监听的流程定义 Key + */ + protected abstract String getProcessDefinitionKey(); + + /** + * 处理事件 + * + * @param event 事件 + */ + protected abstract void onEvent(BpmProcessInstanceResultEvent event); + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/bpm/core/event/BpmProcessInstanceResultEventPublisher.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/bpm/core/event/BpmProcessInstanceResultEventPublisher.java new file mode 100644 index 00000000..59e0a36b --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/bpm/core/event/BpmProcessInstanceResultEventPublisher.java @@ -0,0 +1,24 @@ +package com.ruoyi.flowable.framework.bpm.core.event; + +import lombok.AllArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.validation.annotation.Validated; + +import javax.validation.Valid; + +/** + * {@link BpmProcessInstanceResultEvent} 的生产者 + * + * hasPermi + */ +@AllArgsConstructor +@Validated +public class BpmProcessInstanceResultEventPublisher { + + private final ApplicationEventPublisher publisher; + + public void sendProcessInstanceResultEvent(@Valid BpmProcessInstanceResultEvent event) { + publisher.publishEvent(event); + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/flowable/config/BpmFlowableConfiguration.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/flowable/config/BpmFlowableConfiguration.java new file mode 100644 index 00000000..dec897e6 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/flowable/config/BpmFlowableConfiguration.java @@ -0,0 +1,46 @@ +package com.ruoyi.flowable.framework.flowable.config; + +import cn.hutool.core.collection.ListUtil; +import com.ruoyi.flowable.framework.flowable.core.behavior.BpmActivityBehaviorFactory; +import com.ruoyi.flowable.service.definition.BpmTaskAssignRuleService; +import org.flowable.common.engine.api.delegate.event.FlowableEventListener; +import org.flowable.spring.SpringProcessEngineConfiguration; +import org.flowable.spring.boot.EngineConfigurationConfigurer; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * BPM 模块的 Flowable 配置类 + * + * @author jason + */ +@Configuration +public class BpmFlowableConfiguration { + + /** + * BPM 模块的 ProcessEngineConfigurationConfigurer 实现类: + * + * 1. 设置各种监听器 + * 2. 设置自定义的 ActivityBehaviorFactory 实现 + */ + @Bean + public EngineConfigurationConfigurer bpmProcessEngineConfigurationConfigurer( + ObjectProvider listeners, + BpmActivityBehaviorFactory bpmActivityBehaviorFactory) { + return configuration -> { + // 注册监听器,例如说 BpmActivityEventListener + configuration.setEventListeners(ListUtil.toList(listeners.iterator())); + // 设置 ActivityBehaviorFactory 实现类,用于流程任务的审核人的自定义 + configuration.setActivityBehaviorFactory(bpmActivityBehaviorFactory); + }; + } + + @Bean + public BpmActivityBehaviorFactory bpmActivityBehaviorFactory(BpmTaskAssignRuleService taskRuleService) { + BpmActivityBehaviorFactory bpmActivityBehaviorFactory = new BpmActivityBehaviorFactory(); + bpmActivityBehaviorFactory.setBpmTaskRuleService(taskRuleService); + return bpmActivityBehaviorFactory; + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/flowable/core/behavior/BpmActivityBehaviorFactory.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/flowable/core/behavior/BpmActivityBehaviorFactory.java new file mode 100644 index 00000000..33c8d2f3 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/flowable/core/behavior/BpmActivityBehaviorFactory.java @@ -0,0 +1,46 @@ +package com.ruoyi.flowable.framework.flowable.core.behavior; + +import com.ruoyi.flowable.service.definition.BpmTaskAssignRuleService; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.Setter; +import lombok.ToString; +import org.flowable.bpmn.model.Activity; +import org.flowable.bpmn.model.UserTask; +import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior; +import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior; +import org.flowable.engine.impl.bpmn.behavior.UserTaskActivityBehavior; +import org.flowable.engine.impl.bpmn.parser.factory.DefaultActivityBehaviorFactory; + +/** + * 自定义的 ActivityBehaviorFactory 实现类,目的如下: + * 1. 自定义 {@link #createUserTaskActivityBehavior(UserTask)}:实现自定义的流程任务的 assignee 负责人的分配 + * + * hasPermi + */ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BpmActivityBehaviorFactory extends DefaultActivityBehaviorFactory { + + @Setter + private BpmTaskAssignRuleService bpmTaskRuleService; + + @Override + public UserTaskActivityBehavior createUserTaskActivityBehavior(UserTask userTask) { + BpmUserTaskActivityBehavior bpmUserTaskActivityBehavior = new BpmUserTaskActivityBehavior(userTask); + bpmUserTaskActivityBehavior.setBpmTaskRuleService(bpmTaskRuleService); + return bpmUserTaskActivityBehavior; + } + + @Override + public ParallelMultiInstanceBehavior createParallelMultiInstanceBehavior(Activity activity, + AbstractBpmnActivityBehavior innerActivityBehavior) { + BpmParallelMultiInstanceBehavior bpmParallelMultiInstanceBehavior = new BpmParallelMultiInstanceBehavior(activity, innerActivityBehavior); + bpmParallelMultiInstanceBehavior.setBpmTaskRuleService(bpmTaskRuleService); + return bpmParallelMultiInstanceBehavior; + } + + // TODO @ke:SequentialMultiInstanceBehavior 这个抽空也可以看看 + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java new file mode 100644 index 00000000..3746a4cb --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java @@ -0,0 +1,60 @@ +package com.ruoyi.flowable.framework.flowable.core.behavior; + +import com.ruoyi.flowable.core.utils.FlowableUtils; +import com.ruoyi.flowable.service.definition.BpmTaskAssignRuleService; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.Activity; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior; +import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior; + +import java.util.Set; + +/** + * 自定义的【并行】的【多个】流程任务的 assignee 负责人的分配 + * 第一步,基于分配规则,计算出分配任务的【多个】候选人们。 + * 第二步,将【多个】任务候选人们,设置到 DelegateExecution 的 collectionVariable 变量中,以便 BpmUserTaskActivityBehavior 使用它 + * + * @author kemengkai + * @date 2022-04-21 16:57 + */ +@Slf4j +public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehavior { + + @Setter + private BpmTaskAssignRuleService bpmTaskRuleService; + + public BpmParallelMultiInstanceBehavior(Activity activity, + AbstractBpmnActivityBehavior innerActivityBehavior) { + super(activity, innerActivityBehavior); + } + + /** + * 重写该方法,主要实现两个功能: + * 1. 忽略原有的 collectionVariable、collectionElementVariable 表达式,而是采用自己定义的 + * 2. 获得任务的处理人,并设置到 collectionVariable 中,用于 BpmUserTaskActivityBehavior 从中可以获取任务的处理人 + * + * 注意,多个任务实例,每个任务实例对应一个处理人,所以返回的数量就是任务处理人的数量 + * + * @param execution 执行任务 + * @return 数量 + */ + @Override + protected int resolveNrOfInstances(DelegateExecution execution) { + // 第一步,设置 collectionVariable 和 CollectionVariable + // 从 execution.getVariable() 读取所有任务处理人的 key + super.collectionExpression = null; // collectionExpression 和 collectionVariable 是互斥的 + String cv = FlowableUtils.formatCollectionVariable(execution.getCurrentActivityId()); + super.collectionVariable = cv; + // 从 execution.getVariable() 读取当前所有任务处理的人的 key + super.collectionElementVariable = FlowableUtils.formatCollectionElementVariable(execution.getCurrentActivityId()); + //todo 并签支持,并签的暂无业务所以先注释了 + execution.setVariable("coll_userList", cv); + // 第二步,获取任务的所有处理人 + Set assigneeUserIds = bpmTaskRuleService.calculateTaskCandidateUsers(execution); + execution.setVariable(super.collectionVariable, assigneeUserIds); + return assigneeUserIds.size(); + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/flowable/core/behavior/BpmUserTaskActivityBehavior.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/flowable/core/behavior/BpmUserTaskActivityBehavior.java new file mode 100644 index 00000000..428e1d9f --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/flowable/core/behavior/BpmUserTaskActivityBehavior.java @@ -0,0 +1,65 @@ +package com.ruoyi.flowable.framework.flowable.core.behavior; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.RandomUtil; +import com.ruoyi.flowable.service.definition.BpmTaskAssignRuleService; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.UserTask; +import org.flowable.common.engine.impl.el.ExpressionManager; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.impl.bpmn.behavior.UserTaskActivityBehavior; +import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; +import org.flowable.engine.impl.util.TaskHelper; +import org.flowable.task.service.TaskService; +import org.flowable.task.service.impl.persistence.entity.TaskEntity; + +import java.util.List; +import java.util.Set; + +/** + * 自定义的【单个】流程任务的 assignee 负责人的分配 + * 第一步,基于分配规则,计算出分配任务的【单个】候选人。如果找不到,则直接报业务异常,不继续执行后续的流程; + * 第二步,随机选择一个候选人,则选择作为 assignee 负责人。 + * + * hasPermi + */ +@Slf4j +public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior { + + @Setter + private BpmTaskAssignRuleService bpmTaskRuleService; + + public BpmUserTaskActivityBehavior(UserTask userTask) { + super(userTask); + } + + @Override + protected void handleAssignments(TaskService taskService, String assignee, String owner, + List candidateUsers, List candidateGroups, TaskEntity task, ExpressionManager expressionManager, + DelegateExecution execution, ProcessEngineConfigurationImpl processEngineConfiguration) { + // 第一步,获得任务的候选用户 + Long assigneeUserId = calculateTaskCandidateUsers(execution); + Assert.notNull(assigneeUserId, "任务处理人不能为空"); + // 第二步,设置作为负责人 + TaskHelper.changeTaskAssignee(task, String.valueOf(assigneeUserId)); + } + + private Long calculateTaskCandidateUsers(DelegateExecution execution) { + // 情况一,如果是多实例的任务,例如说会签、或签等情况,则从 Variable 中获取。它的任务处理人在 BpmParallelMultiInstanceBehavior 中已经被分配了 + if (super.multiInstanceActivityBehavior != null) { + return execution.getVariable(super.multiInstanceActivityBehavior.getCollectionElementVariable(), Long.class); + } + // 情况二,如果非多实例的任务,则计算任务处理人 + // 第一步,先计算可处理该任务的处理人们 + Set candidateUserIds = bpmTaskRuleService.calculateTaskCandidateUsers(execution); + // 第二步,后随机选择一个任务的处理人 + // 疑问:为什么一定要选择一个任务处理人? + // 解答:项目对 bpm 的任务是责任到人,所以每个任务有且仅有一个处理人。 + // 如果希望一个任务可以同时被多个人处理,可以考虑使用 BpmParallelMultiInstanceBehavior 实现的会签 or 或签。 + int index = RandomUtil.randomInt(candidateUserIds.size()); + return CollUtil.get(candidateUserIds, index); + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/flowable/core/behavior/script/BpmTaskAssignScript.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/flowable/core/behavior/script/BpmTaskAssignScript.java new file mode 100644 index 00000000..30567f1b --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/flowable/core/behavior/script/BpmTaskAssignScript.java @@ -0,0 +1,34 @@ +package com.ruoyi.flowable.framework.flowable.core.behavior.script; + +import com.ruoyi.flowable.core.enums.definition.BpmTaskRuleScriptEnum; +import org.flowable.engine.delegate.DelegateExecution; + +import java.util.Set; + +/** + * Bpm 任务分配的自定义 Script 脚本 + * 使用场景: + * 1. 设置审批人为发起人 + * 2. 设置审批人为发起人的 Leader + * 3. 甚至审批人为发起人的 Leader 的 Leader + * + * hasPermi + */ +public interface BpmTaskAssignScript { + + /** + * 基于执行任务,获得任务的候选用户们 + * + * @param execution 执行任务 + * @return 候选人用户的编号数组 + */ + Set calculateTaskCandidateUsers(DelegateExecution execution); + + /** + * 获得枚举值 + * + * @return 枚举值 + */ + BpmTaskRuleScriptEnum getEnum(); +} + diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderAbstractScript.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderAbstractScript.java new file mode 100644 index 00000000..a029308c --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderAbstractScript.java @@ -0,0 +1,74 @@ +package com.ruoyi.flowable.framework.flowable.core.behavior.script.impl; + +import com.ruoyi.common.utils.NumberUtils; +import com.ruoyi.flowable.domain.dto.user.AdminUserRespDTO; +import com.ruoyi.flowable.domain.dto.user.DeptRespDTO; +import com.ruoyi.flowable.framework.flowable.core.behavior.script.BpmTaskAssignScript; +import com.ruoyi.flowable.service.task.BpmProcessInstanceService; +import com.ruoyi.flowable.service.user.AdminUserApi; +import com.ruoyi.flowable.service.user.DeptApi; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.context.annotation.Lazy; +import org.springframework.util.Assert; + +import javax.annotation.Resource; +import java.util.Set; + +import static com.ruoyi.common.utils.collection.SetUtils.asSet; +import static java.util.Collections.emptySet; + +/** + * 分配给发起人的 Leader 审批的 Script 实现类 + * 目前 Leader 的定义是, + * + * hasPermi + */ +public abstract class BpmTaskAssignLeaderAbstractScript implements BpmTaskAssignScript { + + @Resource + private AdminUserApi adminUserApi; + @Resource + private DeptApi deptApi; + @Resource + @Lazy // 解决循环依赖 + private BpmProcessInstanceService bpmProcessInstanceService; + + protected Set calculateTaskCandidateUsers(DelegateExecution execution, int level) { + Assert.isTrue(level > 0, "level 必须大于 0"); + // 获得发起人 + ProcessInstance processInstance = bpmProcessInstanceService.getProcessInstance(execution.getProcessInstanceId()); + Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId()); + // 获得对应 leve 的部门 + + DeptRespDTO dept = null; + for (int i = 0; i < level; i++) { + // 获得 level 对应的部门 + if (dept == null) { + dept = getStartUserDept(startUserId); + // 找不到发起人的部门,所以无法使用该规则 + if (dept == null) { + return emptySet(); + } + } else { + DeptRespDTO parentDept = deptApi.getDept(dept.getParentId()); + // 找不到父级部门,所以只好结束寻找。原因是:例如说,级别比较高的人,所在部门层级比较少 + if (parentDept == null) { + break; + } + dept = parentDept; + } + } + return dept.getLeaderUserId() != null ? asSet(dept.getLeaderUserId()) : emptySet(); + } + + private DeptRespDTO getStartUserDept(Long startUserId) { + AdminUserRespDTO startUser = adminUserApi.getUser(startUserId); + // 找不到部门,所以无法使用该规则 + if (startUser.getDeptId() == null) { + return null; + } + return deptApi.getDept(startUser.getDeptId()); + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderX1Script.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderX1Script.java new file mode 100644 index 00000000..efa93cd9 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderX1Script.java @@ -0,0 +1,27 @@ +package com.ruoyi.flowable.framework.flowable.core.behavior.script.impl; + +import com.ruoyi.flowable.core.enums.definition.BpmTaskRuleScriptEnum; +import org.flowable.engine.delegate.DelegateExecution; +import org.springframework.stereotype.Component; + +import java.util.Set; + +/** + * 分配给发起人的一级 Leader 审批的 Script 实现类 + * + * hasPermi + */ +@Component +public class BpmTaskAssignLeaderX1Script extends BpmTaskAssignLeaderAbstractScript { + + @Override + public Set calculateTaskCandidateUsers(DelegateExecution execution) { + return calculateTaskCandidateUsers(execution, 1); + } + + @Override + public BpmTaskRuleScriptEnum getEnum() { + return BpmTaskRuleScriptEnum.LEADER_X1; + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderX2Script.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderX2Script.java new file mode 100644 index 00000000..6e32b39f --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderX2Script.java @@ -0,0 +1,27 @@ +package com.ruoyi.flowable.framework.flowable.core.behavior.script.impl; + +import com.ruoyi.flowable.core.enums.definition.BpmTaskRuleScriptEnum; +import org.flowable.engine.delegate.DelegateExecution; +import org.springframework.stereotype.Component; + +import java.util.Set; + +/** + * 分配给发起人的二级 Leader 审批的 Script 实现类 + * + * hasPermi + */ +@Component +public class BpmTaskAssignLeaderX2Script extends BpmTaskAssignLeaderAbstractScript { + + @Override + public Set calculateTaskCandidateUsers(DelegateExecution execution) { + return calculateTaskCandidateUsers(execution, 2); + } + + @Override + public BpmTaskRuleScriptEnum getEnum() { + return BpmTaskRuleScriptEnum.LEADER_X2; + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/flowable/core/behavior/script/impl/BpmTaskAssignStartUserScript.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/flowable/core/behavior/script/impl/BpmTaskAssignStartUserScript.java new file mode 100644 index 00000000..de64b321 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/flowable/core/behavior/script/impl/BpmTaskAssignStartUserScript.java @@ -0,0 +1,40 @@ +package com.ruoyi.flowable.framework.flowable.core.behavior.script.impl; + +import com.ruoyi.common.utils.NumberUtils; +import com.ruoyi.common.utils.collection.SetUtils; +import com.ruoyi.flowable.core.enums.definition.BpmTaskRuleScriptEnum; +import com.ruoyi.flowable.framework.flowable.core.behavior.script.BpmTaskAssignScript; +import com.ruoyi.flowable.service.task.BpmProcessInstanceService; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.Set; + +/** + * 分配给发起人审批的 Script 实现类 + * + * hasPermi + */ +@Component +public class BpmTaskAssignStartUserScript implements BpmTaskAssignScript { + + @Resource + @Lazy // 解决循环依赖 + private BpmProcessInstanceService bpmProcessInstanceService; + + @Override + public Set calculateTaskCandidateUsers(DelegateExecution execution) { + ProcessInstance processInstance = bpmProcessInstanceService.getProcessInstance(execution.getProcessInstanceId()); + Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId()); + return SetUtils.asSet(startUserId); + } + + @Override + public BpmTaskRuleScriptEnum getEnum() { + return BpmTaskRuleScriptEnum.START_USER; + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/flowable/core/listener/BpmProcessInstanceEventListener.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/flowable/core/listener/BpmProcessInstanceEventListener.java new file mode 100644 index 00000000..22a4964b --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/flowable/core/listener/BpmProcessInstanceEventListener.java @@ -0,0 +1,53 @@ +package com.ruoyi.flowable.framework.flowable.core.listener; + +import com.google.common.collect.ImmutableSet; +import com.ruoyi.flowable.domain.entity.task.BpmProcessInstanceExtDO; +import com.ruoyi.flowable.service.task.BpmProcessInstanceService; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType; +import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener; +import org.flowable.engine.delegate.event.FlowableCancelledEvent; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.Set; + +/** + * 监听 {@link ProcessInstance} 的开始与完成,创建与更新对应的 {@link BpmProcessInstanceExtDO} 记录 + * + * @author jason + */ +@Component +public class BpmProcessInstanceEventListener extends AbstractFlowableEngineEventListener { + + @Resource + @Lazy + private BpmProcessInstanceService processInstanceService; + + public static final Set PROCESS_INSTANCE_EVENTS = ImmutableSet.builder() + .add(FlowableEngineEventType.PROCESS_CREATED) + .add(FlowableEngineEventType.PROCESS_CANCELLED) + .add(FlowableEngineEventType.PROCESS_COMPLETED) + .build(); + + public BpmProcessInstanceEventListener(){ + super(PROCESS_INSTANCE_EVENTS); + } + + @Override + protected void processCreated(FlowableEngineEntityEvent event) { + processInstanceService.createProcessInstanceExt((ProcessInstance)event.getEntity()); + } + + @Override + protected void processCancelled(FlowableCancelledEvent event) { + processInstanceService.updateProcessInstanceExtCancel(event); + } + + @Override + protected void processCompleted(FlowableEngineEntityEvent event) { + processInstanceService.updateProcessInstanceExtComplete((ProcessInstance)event.getEntity()); + } +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/flowable/core/listener/BpmTaskEventListener.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/flowable/core/listener/BpmTaskEventListener.java new file mode 100644 index 00000000..ac81716a --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/framework/flowable/core/listener/BpmTaskEventListener.java @@ -0,0 +1,82 @@ +package com.ruoyi.flowable.framework.flowable.core.listener; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.google.common.collect.ImmutableSet; +import com.ruoyi.flowable.domain.entity.task.BpmTaskExtDO; +import com.ruoyi.flowable.service.task.BpmActivityService; +import com.ruoyi.flowable.service.task.BpmTaskService; +import lombok.extern.slf4j.Slf4j; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType; +import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener; +import org.flowable.engine.delegate.event.FlowableActivityCancelledEvent; +import org.flowable.engine.history.HistoricActivityInstance; +import org.flowable.task.api.Task; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.List; +import java.util.Set; + +/** + * 监听 {@link Task} 的开始与完成,创建与更新对应的 {@link BpmTaskExtDO} 记录 + * + * @author jason + */ +@Component +@Slf4j +public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { + + @Resource + @Lazy // 解决循环依赖 + private BpmTaskService taskService; + + @Resource + @Lazy // 解决循环依赖 + private BpmActivityService activityService; + + public static final Set TASK_EVENTS = ImmutableSet.builder() + .add(FlowableEngineEventType.TASK_CREATED) + .add(FlowableEngineEventType.TASK_ASSIGNED) + .add(FlowableEngineEventType.TASK_COMPLETED) + .add(FlowableEngineEventType.ACTIVITY_CANCELLED) + .build(); + + public BpmTaskEventListener(){ + super(TASK_EVENTS); + } + + @Override + protected void taskCreated(FlowableEngineEntityEvent event) { + taskService.createTaskExt((Task) event.getEntity()); + } + + @Override + protected void taskCompleted(FlowableEngineEntityEvent event) { + taskService.updateTaskExtComplete((Task)event.getEntity()); + } + + @Override + protected void taskAssigned(FlowableEngineEntityEvent event) { + taskService.updateTaskExtAssign((Task)event.getEntity()); + } + + @Override + protected void activityCancelled(FlowableActivityCancelledEvent event) { + List activityList = activityService.getHistoricActivityListByExecutionId(event.getExecutionId()); + if (CollUtil.isEmpty(activityList)) { + log.error("[activityCancelled][使用 executionId({}) 查找不到对应的活动实例]", event.getExecutionId()); + return; + } + // 遍历处理 + activityList.forEach(activity -> { + if (StrUtil.isEmpty(activity.getTaskId())) { + return; + } + taskService.updateTaskExtCancel(activity.getTaskId()); + }); + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/mapper/definition/BpmFormMapper.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/mapper/definition/BpmFormMapper.java new file mode 100644 index 00000000..e2704484 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/mapper/definition/BpmFormMapper.java @@ -0,0 +1,25 @@ +package com.ruoyi.flowable.mapper.definition; + + +import com.ruoyi.common.mybatis.mapper.BaseMapperX; +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.common.mybatis.query.QueryWrapperX; +import com.ruoyi.flowable.domain.entity.definition.BpmFormDO; +import com.ruoyi.flowable.domain.vo.form.BpmFormPageReqVO; +import org.apache.ibatis.annotations.Mapper; + +/** + * 动态表单 Mapper + * + * @author 风里雾里 + */ +@Mapper +public interface BpmFormMapper extends BaseMapperX { + + default PageResult selectPage(BpmFormPageReqVO reqVO) { + return selectPage(reqVO, new QueryWrapperX() + .likeIfPresent("name", reqVO.getName()) + .orderByDesc("id")); + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/mapper/definition/BpmProcessDefinitionExtMapper.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/mapper/definition/BpmProcessDefinitionExtMapper.java new file mode 100644 index 00000000..5415eb31 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/mapper/definition/BpmProcessDefinitionExtMapper.java @@ -0,0 +1,21 @@ +package com.ruoyi.flowable.mapper.definition; + +import com.ruoyi.common.mybatis.mapper.BaseMapperX; +import com.ruoyi.flowable.domain.entity.definition.BpmProcessDefinitionExtDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +@Mapper +public interface BpmProcessDefinitionExtMapper extends BaseMapperX { + + default List selectListByProcessDefinitionIds(Collection processDefinitionIds) { + return selectList("process_definition_id", processDefinitionIds); + } + + default BpmProcessDefinitionExtDO selectByProcessDefinitionId(String processDefinitionId) { + return selectOne("process_definition_id", processDefinitionId); + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/mapper/definition/BpmTaskAssignRuleMapper.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/mapper/definition/BpmTaskAssignRuleMapper.java new file mode 100644 index 00000000..58464030 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/mapper/definition/BpmTaskAssignRuleMapper.java @@ -0,0 +1,37 @@ +package com.ruoyi.flowable.mapper.definition; + +import com.ruoyi.common.mybatis.mapper.BaseMapperX; +import com.ruoyi.common.mybatis.query.QueryWrapperX; +import com.ruoyi.flowable.domain.entity.definition.BpmTaskAssignRuleDO; +import org.apache.ibatis.annotations.Mapper; +import org.springframework.lang.Nullable; + +import java.util.List; + +@Mapper +public interface BpmTaskAssignRuleMapper extends BaseMapperX { + + default List selectListByProcessDefinitionId(String processDefinitionId, + @Nullable String taskDefinitionKey) { + return selectList(new QueryWrapperX() + .eq("process_definition_id", processDefinitionId) + .eqIfPresent("task_definition_key", taskDefinitionKey)); + } + + default List selectListByModelId(String modelId) { + return selectList(new QueryWrapperX() + .eq("model_id", modelId) + .eq("process_definition_id", BpmTaskAssignRuleDO.PROCESS_DEFINITION_ID_NULL)); + } + + default BpmTaskAssignRuleDO selectListByModelIdAndTaskDefinitionKey(String modelId, + String taskDefinitionKey) { + return selectOne(new QueryWrapperX() + .eq("model_id", modelId) + .eq("process_definition_id", BpmTaskAssignRuleDO.PROCESS_DEFINITION_ID_NULL) + .eq("task_definition_key", taskDefinitionKey)); + } + + + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/mapper/definition/BpmUserGroupMapper.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/mapper/definition/BpmUserGroupMapper.java new file mode 100644 index 00000000..fd790984 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/mapper/definition/BpmUserGroupMapper.java @@ -0,0 +1,32 @@ +package com.ruoyi.flowable.mapper.definition; + +import com.ruoyi.common.mybatis.mapper.BaseMapperX; +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.common.mybatis.query.LambdaQueryWrapperX; +import com.ruoyi.flowable.domain.entity.definition.BpmUserGroupDO; +import com.ruoyi.flowable.domain.vo.group.BpmUserGroupPageReqVO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 用户组 Mapper + * + * hasPermi + */ +@Mapper +public interface BpmUserGroupMapper extends BaseMapperX { + + default PageResult selectPage(BpmUserGroupPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(BpmUserGroupDO::getName, reqVO.getName()) + .eqIfPresent(BpmUserGroupDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(BpmUserGroupDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(BpmUserGroupDO::getId)); + } + + default List selectListByStatus(Integer status) { + return selectList(BpmUserGroupDO::getStatus, status); + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/mapper/oa/BpmOALeaveMapper.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/mapper/oa/BpmOALeaveMapper.java new file mode 100644 index 00000000..4e0bb6a2 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/mapper/oa/BpmOALeaveMapper.java @@ -0,0 +1,32 @@ +package com.ruoyi.flowable.mapper.oa; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.ruoyi.common.mybatis.mapper.BaseMapperX; +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.common.mybatis.query.LambdaQueryWrapperX; +import com.ruoyi.flowable.domain.entity.oa.BpmOALeaveDO; +import com.ruoyi.flowable.domain.vo.oa.BpmOALeavePageReqVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** + * 请假申请 Mapper + * + * @author jason + * hasPermi + */ +@Mapper +public interface BpmOALeaveMapper extends BaseMapperX { + + default PageResult selectPage(Long userId, BpmOALeavePageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(BpmOALeaveDO::getUserId, userId) + .eqIfPresent(BpmOALeaveDO::getResult, reqVO.getResult()) + .eqIfPresent(BpmOALeaveDO::getType, reqVO.getType()) + .likeIfPresent(BpmOALeaveDO::getReason, reqVO.getReason()) + .betweenIfPresent(BpmOALeaveDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(BpmOALeaveDO::getId)); + } + + IPage selectBpmOaLeavePage(IPage page,@Param("entity") BpmOALeaveDO bpmOaLeave); +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/mapper/task/BpmProcessInstanceExtMapper.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/mapper/task/BpmProcessInstanceExtMapper.java new file mode 100644 index 00000000..4433b424 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/mapper/task/BpmProcessInstanceExtMapper.java @@ -0,0 +1,34 @@ +package com.ruoyi.flowable.mapper.task; + +import com.ruoyi.common.mybatis.mapper.BaseMapperX; +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.common.mybatis.query.LambdaQueryWrapperX; +import com.ruoyi.flowable.domain.entity.task.BpmProcessInstanceExtDO; +import com.ruoyi.flowable.domain.vo.instance.BpmProcessInstanceMyPageReqVO; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface BpmProcessInstanceExtMapper extends BaseMapperX { + + default PageResult selectPage(Long userId, BpmProcessInstanceMyPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(BpmProcessInstanceExtDO::getStartUserId, userId) + .likeIfPresent(BpmProcessInstanceExtDO::getName, reqVO.getName()) + .eqIfPresent(BpmProcessInstanceExtDO::getProcessDefinitionId, reqVO.getProcessDefinitionId()) + .eqIfPresent(BpmProcessInstanceExtDO::getCategory, reqVO.getCategory()) + .eqIfPresent(BpmProcessInstanceExtDO::getStatus, reqVO.getStatus()) + .eqIfPresent(BpmProcessInstanceExtDO::getResult, reqVO.getResult()) + .betweenIfPresent(BpmProcessInstanceExtDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(BpmProcessInstanceExtDO::getId)); + } + + default BpmProcessInstanceExtDO selectByProcessInstanceId(String processInstanceId) { + return selectOne(BpmProcessInstanceExtDO::getProcessInstanceId, processInstanceId); + } + + default void updateByProcessInstanceId(BpmProcessInstanceExtDO updateObj) { + update(updateObj, new LambdaQueryWrapperX() + .eq(BpmProcessInstanceExtDO::getProcessInstanceId, updateObj.getProcessInstanceId())); + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/mapper/task/BpmTaskExtMapper.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/mapper/task/BpmTaskExtMapper.java new file mode 100644 index 00000000..b1d0db0a --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/mapper/task/BpmTaskExtMapper.java @@ -0,0 +1,26 @@ +package com.ruoyi.flowable.mapper.task; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.ruoyi.common.mybatis.mapper.BaseMapperX; +import com.ruoyi.flowable.domain.entity.task.BpmTaskExtDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +@Mapper +public interface BpmTaskExtMapper extends BaseMapperX { + + default void updateByTaskId(BpmTaskExtDO entity) { + update(entity, new LambdaQueryWrapper().eq(BpmTaskExtDO::getTaskId, entity.getTaskId())); + } + + default List selectListByTaskIds(Collection taskIds) { + return selectList(BpmTaskExtDO::getTaskId, taskIds); + } + + default BpmTaskExtDO selectByTaskId(String taskId) { + return selectOne(BpmTaskExtDO::getTaskId, taskId); + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/BpmFormService.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/BpmFormService.java new file mode 100644 index 00000000..9d561d40 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/BpmFormService.java @@ -0,0 +1,99 @@ +package com.ruoyi.flowable.service.definition; + +import cn.hutool.core.collection.CollUtil; +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.common.utils.collection.CollectionUtils; +import com.ruoyi.flowable.domain.entity.definition.BpmFormDO; +import com.ruoyi.flowable.domain.vo.form.BpmFormCreateReqVO; +import com.ruoyi.flowable.domain.vo.form.BpmFormPageReqVO; +import com.ruoyi.flowable.domain.vo.form.BpmFormUpdateReqVO; + +import javax.validation.Valid; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + + +/** + * 动态表单 Service 接口 + * + * @author @风里雾里 + */ +public interface BpmFormService { + + /** + * 创建动态表单 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createForm(@Valid BpmFormCreateReqVO createReqVO); + + /** + * 更新动态表单 + * + * @param updateReqVO 更新信息 + */ + void updateForm(@Valid BpmFormUpdateReqVO updateReqVO); + + /** + * 删除动态表单 + * + * @param id 编号 + */ + void deleteForm(Long id); + + /** + * 获得动态表单 + * + * @param id 编号 + * @return 动态表单 + */ + BpmFormDO getForm(Long id); + + /** + * 获得动态表单列表 + * + * @return 动态表单列表 + */ + List getFormList(); + + /** + * 获得动态表单列表 + * + * @param ids 编号 + * @return 动态表单列表 + */ + List getFormList(Collection ids); + + /** + * 获得动态表单 Map + * + * @param ids 编号 + * @return 动态表单 Map + */ + default Map getFormMap(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return Collections.emptyMap(); + } + return CollectionUtils.convertMap(this.getFormList(ids), BpmFormDO::getId); + } + + /** + * 获得动态表单分页 + * + * @param pageReqVO 分页查询 + * @return 动态表单分页 + */ + PageResult getFormPage(BpmFormPageReqVO pageReqVO); + + /** + * 校验流程表单已配置 + * + * @param configStr configStr 字段 + * @return 流程表单 + */ + BpmFormDO checkFormConfig(String configStr); + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/BpmModelService.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/BpmModelService.java new file mode 100644 index 00000000..23d84892 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/BpmModelService.java @@ -0,0 +1,86 @@ +package com.ruoyi.flowable.service.definition; + +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.flowable.domain.vo.model.*; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.bpmn.model.FlowNode; + +import javax.validation.Valid; + +/** + * Flowable流程模型接口 + * + * @author yunlongn + */ +public interface BpmModelService { + /** + * 获得流程模型分页 + * + * @param pageVO 分页查询 + * @return 流程模型分页 + */ + PageResult getModelPage(BpmModelPageReqVO pageVO); + + /** + * 创建流程模型 + * + * @param modelVO 创建信息 + * @param bpmnXml BPMN XML + * @return 创建的流程模型的编号 + */ + String createModel(@Valid BpmModelCreateReqVO modelVO, String bpmnXml); + + /** + * 获得流程模块 + * + * @param id 编号 + * @return 流程模型 + */ + BpmModelRespVO getModel(String id); + + /** + * 修改流程模型 + * + * @param updateReqVO 更新信息 + */ + void updateModel(@Valid BpmModelUpdateReqVO updateReqVO); + + /** + * 将流程模型,部署成一个流程定义 + * + * @param id 编号 + */ + void deployModel(String id); + + /** + * 删除模型 + * + * @param id 编号 + */ + void deleteModel(String id); + + /** + * 修改模型的状态,实际更新的部署的流程定义的状态 + * + * @param id 编号 + * @param state 状态 + */ + void updateModelState(String id, Integer state); + + /** + * 获得流程模型编号对应的 BPMN Model + * + * @param id 流程模型编号 + * @return BPMN Model + */ + BpmnModel getBpmnModel(String id); + + /** + * 查找节点 + * @param processDefId 流程定义id + * @param activityId 节点id + * @return + */ + FlowNode findFlowNodeByActivityId(String processDefId, String activityId) ; + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/BpmProcessDefinitionService.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/BpmProcessDefinitionService.java new file mode 100644 index 00000000..6e912659 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/BpmProcessDefinitionService.java @@ -0,0 +1,160 @@ +package com.ruoyi.flowable.service.definition; + +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.common.utils.collection.CollectionUtils; +import com.ruoyi.flowable.domain.dto.definition.BpmProcessDefinitionCreateReqDTO; +import com.ruoyi.flowable.domain.entity.definition.BpmProcessDefinitionExtDO; +import com.ruoyi.flowable.domain.vo.process.BpmProcessDefinitionListReqVO; +import com.ruoyi.flowable.domain.vo.process.BpmProcessDefinitionPageItemRespVO; +import com.ruoyi.flowable.domain.vo.process.BpmProcessDefinitionPageReqVO; +import com.ruoyi.flowable.domain.vo.process.BpmProcessDefinitionRespVO; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.repository.Deployment; +import org.flowable.engine.repository.ProcessDefinition; + +import javax.validation.Valid; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Flowable流程定义接口 + * + * @author yunlong.li + * @author ZJQ + * hasPermi + */ +public interface BpmProcessDefinitionService { + + /** + * 获得流程定义分页 + * + * @param pageReqVO 分页入参 + * @return 流程定义 Page + */ + PageResult getProcessDefinitionPage(BpmProcessDefinitionPageReqVO pageReqVO); + + /** + * 获得流程定义列表 + * + * @param listReqVO 列表入参 + * @return 流程定义列表 + */ + List getProcessDefinitionList(BpmProcessDefinitionListReqVO listReqVO); + + /** + * 创建流程定义 + * + * @param createReqDTO 创建信息 + * @return 流程编号 + */ + String createProcessDefinition(@Valid BpmProcessDefinitionCreateReqDTO createReqDTO); + + /** + * 更新流程定义状态 + * + * @param id 流程定义的编号 + * @param state 状态 + */ + void updateProcessDefinitionState(String id, Integer state); + + /** + * 获得流程定义对应的 BPMN XML + * + * @param id 流程定义编号 + * @return BPMN XML + */ + String getProcessDefinitionBpmnXML(String id); + + /** + * 获得需要创建的流程定义,是否和当前激活的流程定义相等 + * + * @param createReqDTO 创建信息 + * @return 是否相等 + */ + boolean isProcessDefinitionEquals(@Valid BpmProcessDefinitionCreateReqDTO createReqDTO); + + /** + * 获得编号对应的 BpmProcessDefinitionExtDO + * + * @param id 编号 + * @return 流程定义拓展 + */ + BpmProcessDefinitionExtDO getProcessDefinitionExt(String id); + + /** + * 获得编号对应的 ProcessDefinition + * + * @param id 编号 + * @return 流程定义 + */ + ProcessDefinition getProcessDefinition(String id); + + /** + * 获得编号对应的 ProcessDefinition + * + * 相比 {@link #getProcessDefinition(String)} 方法,category 的取值是正确 + * + * @param id 编号 + * @return 流程定义 + */ + ProcessDefinition getProcessDefinition2(String id); + + /** + * 获得 deploymentId 对应的 ProcessDefinition + * + * @param deploymentId 部署编号 + * @return 流程定义 + */ + ProcessDefinition getProcessDefinitionByDeploymentId(String deploymentId); + + /** + * 获得 deploymentIds 对应的 ProcessDefinition 数组 + * + * @param deploymentIds 部署编号的数组 + * @return 流程定义的数组 + */ + List getProcessDefinitionListByDeploymentIds(Set deploymentIds); + + /** + * 获得流程定义标识对应的激活的流程定义 + * + * @param key 流程定义的标识 + * @return 流程定义 + */ + ProcessDefinition getActiveProcessDefinition(String key); + + /** + * 获得 ids 对应的 Deployment Map + * + * @param ids 部署编号的数组 + * @return 流程部署 Map + */ + default Map getDeploymentMap(Set ids) { + return CollectionUtils.convertMap(getDeployments(ids), Deployment::getId); + } + + /** + * 获得 ids 对应的 Deployment 数组 + * + * @param ids 部署编号的数组 + * @return 流程部署的数组 + */ + List getDeployments(Set ids); + + /** + * 获得 id 对应的 Deployment + * + * @param id 部署编号 + * @return 流程部署 + */ + Deployment getDeployment(String id); + + /** + * 获得 Bpmn 模型 + * + * @param processDefinitionId 流程定义的编号 + * @return Bpmn 模型 + */ + BpmnModel getBpmnModel(String processDefinitionId); +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/BpmProcessInstanceApi.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/BpmProcessInstanceApi.java new file mode 100644 index 00000000..1ab0d7c8 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/BpmProcessInstanceApi.java @@ -0,0 +1,24 @@ +package com.ruoyi.flowable.service.definition; + + +import com.ruoyi.flowable.domain.dto.task.BpmProcessInstanceCreateReqDTO; + +import javax.validation.Valid; + +/** + * 流程实例 Api 接口 + * + * hasPermi + */ +public interface BpmProcessInstanceApi { + + /** + * 创建流程实例(提供给内部) + * + * @param userId 用户编号 + * @param reqDTO 创建信息 + * @return 实例的编号 + */ + String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO reqDTO); + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/BpmTaskAssignRuleService.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/BpmTaskAssignRuleService.java new file mode 100644 index 00000000..c2710312 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/BpmTaskAssignRuleService.java @@ -0,0 +1,97 @@ +package com.ruoyi.flowable.service.definition; + +import com.ruoyi.flowable.domain.entity.definition.BpmTaskAssignRuleDO; +import com.ruoyi.flowable.domain.vo.rule.BpmTaskAssignRuleCreateReqVO; +import com.ruoyi.flowable.domain.vo.rule.BpmTaskAssignRuleRespVO; +import com.ruoyi.flowable.domain.vo.rule.BpmTaskAssignRuleUpdateReqVO; +import org.flowable.engine.delegate.DelegateExecution; +import org.springframework.lang.Nullable; + +import javax.validation.Valid; +import java.util.List; +import java.util.Set; + +/** + * BPM 任务分配规则 Service 接口 + * + * hasPermi + */ +public interface BpmTaskAssignRuleService { + + /** + * 获得流程定义的任务分配规则数组 + * + * @param processDefinitionId 流程定义的编号 + * @param taskDefinitionKey 流程任务定义的 Key。允许空 + * @return 任务规则数组 + */ + List getTaskAssignRuleListByProcessDefinitionId(String processDefinitionId, + @Nullable String taskDefinitionKey); + + /** + * 获得流程模型的任务规则数组 + * + * @param modelId 流程模型的编号 + * @return 任务规则数组 + */ + List getTaskAssignRuleListByModelId(String modelId); + + /** + * 获得流程定义的任务分配规则数组 + * + * @param modelId 流程模型的编号 + * @param processDefinitionId 流程定义的编号 + * @return 任务规则数组 + */ + List getTaskAssignRuleList(String modelId, String processDefinitionId); + + /** + * 创建任务分配规则 + * + * @param reqVO 创建信息 + * @return 规则编号 + */ + Long createTaskAssignRule(@Valid BpmTaskAssignRuleCreateReqVO reqVO); + + /** + * 更新任务分配规则 + * + * @param reqVO 创建信息 + */ + void updateTaskAssignRule(@Valid BpmTaskAssignRuleUpdateReqVO reqVO); + + /** + * 判断指定流程模型和流程定义的分配规则是否相等 + * + * @param modelId 流程模型编号 + * @param processDefinitionId 流程定义编号 + * @return 是否相等 + */ + boolean isTaskAssignRulesEquals(String modelId, String processDefinitionId); + + /** + * 将流程流程模型的任务分配规则,复制一份给流程定义 + * 目的:每次流程模型部署时,都会生成一个新的流程定义,此时考虑到每次部署的流程不可变性,所以需要复制一份给该流程定义 + * + * @param fromModelId 流程模型编号 + * @param toProcessDefinitionId 流程定义编号 + */ + void copyTaskAssignRules(String fromModelId, String toProcessDefinitionId); + + /** + * 校验流程模型的任务分配规则全部都配置了 + * 目的:如果有规则未配置,会导致流程任务找不到负责人,进而流程无法进行下去! + * + * @param id 流程模型编号 + */ + void checkTaskAssignRuleAllConfig(String id); + + /** + * 计算当前执行任务的处理人 + * + * @param execution 执行任务 + * @return 处理人的编号数组 + */ + Set calculateTaskCandidateUsers(DelegateExecution execution); + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/BpmUserGroupService.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/BpmUserGroupService.java new file mode 100644 index 00000000..8556cbcd --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/BpmUserGroupService.java @@ -0,0 +1,84 @@ +package com.ruoyi.flowable.service.definition; + +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.flowable.domain.entity.definition.BpmUserGroupDO; +import com.ruoyi.flowable.domain.vo.group.BpmUserGroupCreateReqVO; +import com.ruoyi.flowable.domain.vo.group.BpmUserGroupPageReqVO; +import com.ruoyi.flowable.domain.vo.group.BpmUserGroupUpdateReqVO; + +import javax.validation.Valid; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +/** + * 用户组 Service 接口 + * + * hasPermi + */ +public interface BpmUserGroupService { + + /** + * 创建用户组 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createUserGroup(@Valid BpmUserGroupCreateReqVO createReqVO); + + /** + * 更新用户组 + * + * @param updateReqVO 更新信息 + */ + void updateUserGroup(@Valid BpmUserGroupUpdateReqVO updateReqVO); + + /** + * 删除用户组 + * + * @param id 编号 + */ + void deleteUserGroup(Long id); + + /** + * 获得用户组 + * + * @param id 编号 + * @return 用户组 + */ + BpmUserGroupDO getUserGroup(Long id); + + /** + * 获得用户组列表 + * + * @param ids 编号 + * @return 用户组列表 + */ + List getUserGroupList(Collection ids); + + /** + * 获得指定状态的用户组列表 + * + * @param status 状态 + * @return 用户组列表 + */ + List getUserGroupListByStatus(Integer status); + + /** + * 获得用户组分页 + * + * @param pageReqVO 分页查询 + * @return 用户组分页 + */ + PageResult getUserGroupPage(BpmUserGroupPageReqVO pageReqVO); + + /** + * 校验用户组们是否有效。如下情况,视为无效: + * 1. 用户组编号不存在 + * 2. 用户组被禁用 + * + * @param ids 用户组编号数组 + */ + void validUserGroups(Set ids); + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/impl/BpmFormServiceImpl.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/impl/BpmFormServiceImpl.java new file mode 100644 index 00000000..eba49101 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/impl/BpmFormServiceImpl.java @@ -0,0 +1,137 @@ +package com.ruoyi.flowable.service.definition.impl; + +import cn.hutool.core.lang.Assert; +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.common.utils.JsonUtils; +import com.ruoyi.common.utils.ValidationUtils; +import com.ruoyi.flowable.convert.definition.BpmFormConvert; +import com.ruoyi.flowable.core.enums.ErrorCodeConstants; +import com.ruoyi.flowable.core.enums.definition.BpmModelFormTypeEnum; +import com.ruoyi.flowable.domain.dto.definition.BpmFormFieldRespDTO; +import com.ruoyi.flowable.domain.dto.definition.BpmModelMetaInfoRespDTO; +import com.ruoyi.flowable.domain.entity.definition.BpmFormDO; +import com.ruoyi.flowable.domain.vo.form.BpmFormCreateReqVO; +import com.ruoyi.flowable.domain.vo.form.BpmFormPageReqVO; +import com.ruoyi.flowable.domain.vo.form.BpmFormUpdateReqVO; +import com.ruoyi.flowable.mapper.definition.BpmFormMapper; +import com.ruoyi.flowable.service.definition.BpmFormService; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.util.*; + +import static com.ruoyi.common.exception.util.ServiceExceptionUtil.exception; +import static com.ruoyi.flowable.core.enums.ErrorCodeConstants.*; + + +/** + * 动态表单 Service 实现类 + * + * @author 风里雾里 + */ +@Service +@Validated +public class BpmFormServiceImpl implements BpmFormService { + + @Resource + private BpmFormMapper formMapper; + + @Override + public Long createForm(BpmFormCreateReqVO createReqVO) { + // 插入 + BpmFormDO form = BpmFormConvert.INSTANCE.convert(createReqVO); + formMapper.insert(form); + // 返回 + return form.getId(); + } + + @Override + public void updateForm(BpmFormUpdateReqVO updateReqVO) { + // 校验存在 + this.validateFormExists(updateReqVO.getId()); + // 更新 + BpmFormDO updateObj = BpmFormConvert.INSTANCE.convert(updateReqVO); + formMapper.updateById(updateObj); + } + + @Override + public void deleteForm(Long id) { + // 校验存在 + this.validateFormExists(id); + // 删除 + formMapper.deleteById(id); + } + + private void validateFormExists(Long id) { + if (formMapper.selectById(id) == null) { + throw exception(ErrorCodeConstants.FORM_NOT_EXISTS); + } + } + + @Override + public BpmFormDO getForm(Long id) { + return formMapper.selectById(id); + } + + @Override + public List getFormList() { + return formMapper.selectList(); + } + + @Override + public List getFormList(Collection ids) { + return formMapper.selectBatchIds(ids); + } + + @Override + public PageResult getFormPage(BpmFormPageReqVO pageReqVO) { + return formMapper.selectPage(pageReqVO); + } + + + @Override + public BpmFormDO checkFormConfig(String configStr) { + BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(configStr, BpmModelMetaInfoRespDTO.class); + if (metaInfo == null || metaInfo.getFormType() == null) { + throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG); + } + // 校验表单存在 + if (Objects.equals(metaInfo.getFormType(), BpmModelFormTypeEnum.NORMAL.getType())) { + BpmFormDO form = getForm(metaInfo.getFormId()); + if (form == null) { + throw exception(FORM_NOT_EXISTS); + } + return form; + } + return null; + } + + private void checkKeyNCName(String key) { + if (!ValidationUtils.isXmlNCName(key)) { + throw exception(MODEL_KEY_VALID); + } + } + + /** + * 校验 Field,避免 field 重复 + * + * @param fields field 数组 + */ + private void checkFields(List fields) { + // key 是 vModel,value 是 label + Map fieldMap = new HashMap<>(); + for (String field : fields) { + BpmFormFieldRespDTO fieldDTO = JsonUtils.parseObject(field, BpmFormFieldRespDTO.class); + Assert.notNull(fieldDTO); + String oldLabel = fieldMap.put(fieldDTO.getVModel(), fieldDTO.getLabel()); + // 如果不存在,则直接返回 + if (oldLabel == null) { + continue; + } + // 如果存在,则报错 + throw exception(ErrorCodeConstants.FORM_FIELD_REPEAT, oldLabel, fieldDTO.getLabel(), fieldDTO.getVModel()); + } + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/impl/BpmModelServiceImpl.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/impl/BpmModelServiceImpl.java new file mode 100644 index 00000000..c9d53d44 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/impl/BpmModelServiceImpl.java @@ -0,0 +1,311 @@ +package com.ruoyi.flowable.service.definition.impl; + +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.StrUtil; +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.common.utils.JsonUtils; +import com.ruoyi.common.utils.PageUtils; +import com.ruoyi.common.utils.ValidationUtils; +import com.ruoyi.common.utils.collection.CollectionUtils; +import com.ruoyi.flowable.convert.definition.BpmModelConvert; +import com.ruoyi.flowable.core.enums.definition.BpmModelFormTypeEnum; +import com.ruoyi.flowable.domain.dto.definition.BpmModelMetaInfoRespDTO; +import com.ruoyi.flowable.domain.dto.definition.BpmProcessDefinitionCreateReqDTO; +import com.ruoyi.flowable.domain.entity.definition.BpmFormDO; +import com.ruoyi.flowable.domain.vo.model.*; +import com.ruoyi.flowable.service.definition.BpmFormService; +import com.ruoyi.flowable.service.definition.BpmModelService; +import com.ruoyi.flowable.service.definition.BpmProcessDefinitionService; +import com.ruoyi.flowable.service.definition.BpmTaskAssignRuleService; +import com.ruoyi.plugin.tenant.core.context.TenantContextHolder; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.converter.BpmnXMLConverter; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.bpmn.model.FlowElement; +import org.flowable.bpmn.model.FlowNode; +import org.flowable.bpmn.model.Process; +import org.flowable.common.engine.impl.db.SuspensionState; +import org.flowable.common.engine.impl.util.io.BytesStreamSource; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.repository.Deployment; +import org.flowable.engine.repository.Model; +import org.flowable.engine.repository.ModelQuery; +import org.flowable.engine.repository.ProcessDefinition; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.ObjectUtils; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.*; + +import static com.ruoyi.common.exception.util.ServiceExceptionUtil.exception; +import static com.ruoyi.common.utils.collection.CollectionUtils.convertMap; +import static com.ruoyi.flowable.core.enums.ErrorCodeConstants.*; + +/** + * Flowable流程模型实现 + * 主要进行 Flowable {@link Model} 的维护 + */ +@Service +@Validated +@Slf4j +public class BpmModelServiceImpl implements BpmModelService { + + @Resource + private RepositoryService repositoryService; + @Resource + private BpmProcessDefinitionService processDefinitionService; + @Resource + private BpmFormService bpmFormService; + @Resource + private BpmTaskAssignRuleService taskAssignRuleService; + + @Override + public PageResult getModelPage(BpmModelPageReqVO pageVO) { + ModelQuery modelQuery = repositoryService.createModelQuery(); + if (StrUtil.isNotBlank(pageVO.getKey())) { + modelQuery.modelKey(pageVO.getKey()); + } + if (StrUtil.isNotBlank(pageVO.getName())) { + // 模糊匹配 + modelQuery.modelNameLike("%" + pageVO.getName() + "%"); + } + if (StrUtil.isNotBlank(pageVO.getCategory())) { + modelQuery.modelCategory(pageVO.getCategory()); + } + modelQuery.modelTenantId(TenantContextHolder.getTenantId().toString()); + // 执行查询 + List models = modelQuery.orderByCreateTime().desc() + .listPage(PageUtils.getStart(pageVO), pageVO.getPageSize()); + + // 获得 Form Map + Set formIds = CollectionUtils.convertSet(models, model -> { + BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoRespDTO.class); + return metaInfo != null ? metaInfo.getFormId() : null; + }); + Map formMap = bpmFormService.getFormMap(formIds); + + // 获得 Deployment Map + Set deploymentIds = new HashSet<>(); + models.forEach(model -> CollectionUtils.addIfNotNull(deploymentIds, model.getDeploymentId())); + Map deploymentMap = processDefinitionService.getDeploymentMap(deploymentIds); + // 获得 ProcessDefinition Map + List processDefinitions = processDefinitionService.getProcessDefinitionListByDeploymentIds(deploymentIds); + Map processDefinitionMap = convertMap(processDefinitions, ProcessDefinition::getDeploymentId); + + // 拼接结果 + long modelCount = modelQuery.count(); + return new PageResult<>(BpmModelConvert.INSTANCE.convertList(models, formMap, deploymentMap, processDefinitionMap), modelCount); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public String createModel(@Valid BpmModelCreateReqVO createReqVO, String bpmnXml) { + checkKeyNCName(createReqVO.getKey()); + // 校验流程标识已经存在666 + Model keyModel = getModelByKey(createReqVO.getKey()); + if (keyModel != null) { + throw exception(MODEL_KEY_EXISTS, createReqVO.getKey()); + } + + // 创建流程定义 + Model model = repositoryService.newModel(); + BpmModelConvert.INSTANCE.copy(model, createReqVO); + model.setTenantId(TenantContextHolder.getTenantId().toString()); + // 保存流程定义 + repositoryService.saveModel(model); + // 保存 BPMN XML + saveModelBpmnXml(model, bpmnXml); + return model.getId(); + } + + private Model getModelByKey(String key) { + return repositoryService.createModelQuery().modelKey(key).modelTenantId(TenantContextHolder.getTenantId().toString()).singleResult(); + } + + @Override + public BpmModelRespVO getModel(String id) { + Model model = repositoryService.getModel(id); + if (model == null) { + return null; + } + BpmModelRespVO modelRespVO = BpmModelConvert.INSTANCE.convert(model); + // 拼接 bpmn XML + byte[] bpmnBytes = repositoryService.getModelEditorSource(id); + modelRespVO.setBpmnXml(StrUtil.utf8Str(bpmnBytes)); + return modelRespVO; + } + + @Override + @Transactional(rollbackFor = Exception.class) // 因为进行多个操作,所以开启事务 + public void updateModel(@Valid BpmModelUpdateReqVO updateReqVO) { + // 校验流程模型存在 + Model model = repositoryService.getModel(updateReqVO.getId()); + if (model == null) { + throw exception(MODEL_NOT_EXISTS); + } + + // 修改流程定义 + BpmModelConvert.INSTANCE.copy(model, updateReqVO); + // 更新模型 + repositoryService.saveModel(model); + // 更新 BPMN XML + saveModelBpmnXml(model, updateReqVO.getBpmnXml()); + } + + @Override + @Transactional(rollbackFor = Exception.class) // 因为进行多个操作,所以开启事务 + public void deployModel(String id) { + // 1.1 校验流程模型存在 + Model model = repositoryService.getModel(id); + if (ObjectUtils.isEmpty(model)) { + throw exception(MODEL_NOT_EXISTS); + } + // 1.2 校验流程图 + // TODO 芋艿:校验流程图的有效性;例如说,是否有开始的元素,是否有结束的元素; + byte[] bpmnBytes = repositoryService.getModelEditorSource(model.getId()); + if (bpmnBytes == null) { + throw exception(MODEL_NOT_EXISTS); + } + // 1.3 校验表单已配 + BpmFormDO form = checkFormConfig(model.getMetaInfo()); + // 1.4 校验任务分配规则已配置 + taskAssignRuleService.checkTaskAssignRuleAllConfig(id); + + // 1.5 校验模型是否发生修改。如果未修改,则不允许创建 + BpmProcessDefinitionCreateReqDTO definitionCreateReqDTO = BpmModelConvert.INSTANCE.convert2(model, form).setBpmnBytes(bpmnBytes); + // 流程定义的信息相等 + if (processDefinitionService.isProcessDefinitionEquals(definitionCreateReqDTO)) { + ProcessDefinition oldProcessDefinition = processDefinitionService.getProcessDefinitionByDeploymentId(model.getDeploymentId()); + if (oldProcessDefinition != null && taskAssignRuleService.isTaskAssignRulesEquals(model.getId(), oldProcessDefinition.getId())) { + throw exception(MODEL_DEPLOY_FAIL_TASK_INFO_EQUALS); + } + } + + // 2.1 创建流程定义 + String definitionId = processDefinitionService.createProcessDefinition(definitionCreateReqDTO); + + // 2.2 将老的流程定义进行挂起。也就是说,只有最新部署的流程定义,才可以发起任务。 + updateProcessDefinitionSuspended(model.getDeploymentId()); + + // 2.3 更新 model 的 deploymentId,进行关联 + ProcessDefinition definition = processDefinitionService.getProcessDefinition(definitionId); + model.setDeploymentId(definition.getDeploymentId()); + repositoryService.saveModel(model); + + // 2.4 复制任务分配规则 + taskAssignRuleService.copyTaskAssignRules(id, definition.getId()); + } + + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteModel(String id) { + // 校验流程模型存在 + Model model = repositoryService.getModel(id); + if (model == null) { + throw exception(MODEL_NOT_EXISTS); + } + // 执行删除 + repositoryService.deleteModel(id); + // 禁用流程实例 + updateProcessDefinitionSuspended(model.getDeploymentId()); + } + + @Override + public void updateModelState(String id, Integer state) { + // 校验流程模型存在 + Model model = repositoryService.getModel(id); + if (model == null) { + throw exception(MODEL_NOT_EXISTS); + } + // 校验流程定义存在 + ProcessDefinition definition = processDefinitionService.getProcessDefinitionByDeploymentId(model.getDeploymentId()); + if (definition == null) { + throw exception(PROCESS_DEFINITION_NOT_EXISTS); + } + + // 更新状态 + processDefinitionService.updateProcessDefinitionState(definition.getId(), state); + } + + @Override + public BpmnModel getBpmnModel(String id) { + byte[] bpmnBytes = repositoryService.getModelEditorSource(id); + if (ArrayUtil.isEmpty(bpmnBytes)) { + return null; + } + BpmnXMLConverter converter = new BpmnXMLConverter(); + return converter.convertToBpmnModel(new BytesStreamSource(bpmnBytes), true, true); + } + + @Override + public FlowNode findFlowNodeByActivityId(String processDefId, String activityId) { + FlowNode activity = null; + BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefId); + List processes = bpmnModel.getProcesses(); + for (Process process : processes) { + FlowElement flowElement = process.getFlowElementMap().get(activityId); + if (flowElement != null) { + activity = (FlowNode) flowElement; + break; + } + } + return activity; + } + + private void checkKeyNCName(String key) { + if (!ValidationUtils.isXmlNCName(key)) { + throw exception(MODEL_KEY_VALID); + } + } + + /** + * 校验流程表单已配置 + * + * @param metaInfoStr 流程模型 metaInfo 字段 + * @return 流程表单 + */ + private BpmFormDO checkFormConfig(String metaInfoStr) { + BpmModelMetaInfoRespDTO metaInfo = JsonUtils.parseObject(metaInfoStr, BpmModelMetaInfoRespDTO.class); + if (metaInfo == null || metaInfo.getFormType() == null) { + throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG); + } + // 校验表单存在 + if (Objects.equals(metaInfo.getFormType(), BpmModelFormTypeEnum.NORMAL.getType())) { + BpmFormDO form = bpmFormService.getForm(metaInfo.getFormId()); + if (form == null) { + throw exception(FORM_NOT_EXISTS); + } + return form; + } + return null; + } + + private void saveModelBpmnXml(Model model, String bpmnXml) { + if (StrUtil.isEmpty(bpmnXml)) { + return; + } + repositoryService.addModelEditorSource(model.getId(), StrUtil.utf8Bytes(bpmnXml)); + } + + /** + * 挂起 deploymentId 对应的流程定义。 这里一个deploymentId 只关联一个流程定义 + * + * @param deploymentId 流程发布Id. + */ + private void updateProcessDefinitionSuspended(String deploymentId) { + if (StrUtil.isEmpty(deploymentId)) { + return; + } + ProcessDefinition oldDefinition = processDefinitionService.getProcessDefinitionByDeploymentId(deploymentId); + if (oldDefinition == null) { + return; + } + processDefinitionService.updateProcessDefinitionState(oldDefinition.getId(), SuspensionState.SUSPENDED.getStateCode()); + } + + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/impl/BpmProcessDefinitionServiceImpl.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/impl/BpmProcessDefinitionServiceImpl.java new file mode 100644 index 00000000..2d4536a2 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/impl/BpmProcessDefinitionServiceImpl.java @@ -0,0 +1,288 @@ +package com.ruoyi.flowable.service.definition.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.common.utils.PageUtils; +import com.ruoyi.flowable.convert.definition.BpmProcessDefinitionConvert; +import com.ruoyi.flowable.core.utils.FlowableUtils; +import com.ruoyi.flowable.domain.dto.definition.BpmProcessDefinitionCreateReqDTO; +import com.ruoyi.flowable.domain.entity.definition.BpmFormDO; +import com.ruoyi.flowable.domain.entity.definition.BpmProcessDefinitionExtDO; +import com.ruoyi.flowable.domain.vo.process.BpmProcessDefinitionListReqVO; +import com.ruoyi.flowable.domain.vo.process.BpmProcessDefinitionPageItemRespVO; +import com.ruoyi.flowable.domain.vo.process.BpmProcessDefinitionPageReqVO; +import com.ruoyi.flowable.domain.vo.process.BpmProcessDefinitionRespVO; +import com.ruoyi.flowable.mapper.definition.BpmProcessDefinitionExtMapper; +import com.ruoyi.flowable.service.definition.BpmFormService; +import com.ruoyi.flowable.service.definition.BpmProcessDefinitionService; +import com.ruoyi.plugin.tenant.core.context.TenantContextHolder; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.converter.BpmnXMLConverter; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.common.engine.impl.db.SuspensionState; +import org.flowable.common.engine.impl.util.io.BytesStreamSource; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.repository.Deployment; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.engine.repository.ProcessDefinitionQuery; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.*; + +import static com.ruoyi.common.exception.util.ServiceExceptionUtil.exception; +import static com.ruoyi.common.utils.collection.CollectionUtils.*; +import static com.ruoyi.flowable.core.enums.ErrorCodeConstants.PROCESS_DEFINITION_KEY_NOT_MATCH; +import static com.ruoyi.flowable.core.enums.ErrorCodeConstants.PROCESS_DEFINITION_NAME_NOT_MATCH; +import static java.util.Collections.emptyList; + +/** + * 流程定义实现 + * 主要进行 Flowable {@link ProcessDefinition} 和 {@link Deployment} 的维护 + * + * @author yunlongn + * @author ZJQ + * hasPermi + */ +@Service +@Validated +@Slf4j +public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionService { + + private static final String BPMN_FILE_SUFFIX = ".bpmn"; + + @Resource + private RepositoryService repositoryService; + + @Resource + private BpmProcessDefinitionExtMapper processDefinitionMapper; + + @Resource + private BpmFormService formService; + + @Override + public ProcessDefinition getProcessDefinition(String id) { + return repositoryService.getProcessDefinition(id); + } + + @Override + public ProcessDefinition getProcessDefinition2(String id) { + return repositoryService.createProcessDefinitionQuery().processDefinitionId(id).singleResult(); + } + + @Override + public ProcessDefinition getProcessDefinitionByDeploymentId(String deploymentId) { + if (StrUtil.isEmpty(deploymentId)) { + return null; + } + return repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult(); + } + + @Override + public List getProcessDefinitionListByDeploymentIds(Set deploymentIds) { + if (CollUtil.isEmpty(deploymentIds)) { + return emptyList(); + } + return repositoryService.createProcessDefinitionQuery().deploymentIds(deploymentIds).list(); + } + + @Override + public ProcessDefinition getActiveProcessDefinition(String key) { + return repositoryService.createProcessDefinitionQuery().processDefinitionKey(key).active().singleResult(); + } + + @Override + public List getDeployments(Set ids) { + if (CollUtil.isEmpty(ids)) { + return emptyList(); + } + List list = new ArrayList<>(ids.size()); + for (String id : ids) { + addIfNotNull(list, getDeployment(id)); + } + return list; + } + + @Override + public Deployment getDeployment(String id) { + if (StrUtil.isEmpty(id)) { + return null; + } + return repositoryService.createDeploymentQuery().deploymentId(id).singleResult(); + } + + @Override + public BpmnModel getBpmnModel(String processDefinitionId) { + return repositoryService.getBpmnModel(processDefinitionId); + } + + @Override + public String createProcessDefinition(@Valid BpmProcessDefinitionCreateReqDTO createReqDTO) { + // 创建 Deployment 部署 + Deployment deploy = repositoryService.createDeployment() + .key(createReqDTO.getKey()).name(createReqDTO.getName()).category(createReqDTO.getCategory()).tenantId(TenantContextHolder.getTenantId().toString()) + .addBytes(createReqDTO.getKey() + BPMN_FILE_SUFFIX, createReqDTO.getBpmnBytes()) + .deploy(); + + // 设置 ProcessDefinition 的 category 分类 + ProcessDefinition definition = repositoryService.createProcessDefinitionQuery() + .deploymentId(deploy.getId()).singleResult(); + repositoryService.setProcessDefinitionCategory(definition.getId(), createReqDTO.getCategory()); + // 注意 1,ProcessDefinition 的 key 和 name 是通过 BPMN 中的 的 id 和 name 决定 + // 注意 2,目前该项目的设计上,需要保证 Model、Deployment、ProcessDefinition 使用相同的 key,保证关联性。 + // 否则,会导致 ProcessDefinition 的分页无法查询到。 + if (!Objects.equals(definition.getKey(), createReqDTO.getKey())) { + throw exception(PROCESS_DEFINITION_KEY_NOT_MATCH, createReqDTO.getKey(), definition.getKey()); + } + if (!Objects.equals(definition.getName(), createReqDTO.getName())) { + throw exception(PROCESS_DEFINITION_NAME_NOT_MATCH, createReqDTO.getName(), definition.getName()); + } + + // 插入拓展表 + BpmProcessDefinitionExtDO definitionDO = BpmProcessDefinitionConvert.INSTANCE.convert2(createReqDTO); + definitionDO.setProcessDefinitionId(definition.getId()); + processDefinitionMapper.insert(definitionDO); + return definition.getId(); + } + + @Override + public void updateProcessDefinitionState(String id, Integer state) { + // 激活 + if (Objects.equals(SuspensionState.ACTIVE.getStateCode(), state)) { + repositoryService.activateProcessDefinitionById(id, false, null); + return; + } + // 挂起 + if (Objects.equals(SuspensionState.SUSPENDED.getStateCode(), state)) { + // suspendProcessInstances = false,进行中的任务,不进行挂起。 + // 原因:只要新的流程不允许发起即可,老流程继续可以执行。 + repositoryService.suspendProcessDefinitionById(id, false, null); + return; + } + log.error("[updateProcessDefinitionState][流程定义({}) 修改未知状态({})]", id, state); + } + + @Override + public String getProcessDefinitionBpmnXML(String id) { + BpmnModel bpmnModel = repositoryService.getBpmnModel(id); + if (bpmnModel == null) { + return null; + } + BpmnXMLConverter converter = new BpmnXMLConverter(); + return StrUtil.utf8Str(converter.convertToXML(bpmnModel)); + } + + @Override + public boolean isProcessDefinitionEquals(@Valid BpmProcessDefinitionCreateReqDTO createReqDTO) { + // 校验 name、description 是否更新 + ProcessDefinition oldProcessDefinition = getActiveProcessDefinition(createReqDTO.getKey()); + if (oldProcessDefinition == null) { + return false; + } + BpmProcessDefinitionExtDO oldProcessDefinitionExt = getProcessDefinitionExt(oldProcessDefinition.getId()); + if (!StrUtil.equals(createReqDTO.getName(), oldProcessDefinition.getName()) + || !StrUtil.equals(createReqDTO.getDescription(), oldProcessDefinitionExt.getDescription()) + || !StrUtil.equals(createReqDTO.getCategory(), oldProcessDefinition.getCategory())) { + return false; + } + // 校验 form 信息是否更新 + if (!ObjectUtil.equal(createReqDTO.getFormType(), oldProcessDefinitionExt.getFormType()) + || !ObjectUtil.equal(createReqDTO.getFormId(), oldProcessDefinitionExt.getFormId()) + || !ObjectUtil.equal(createReqDTO.getFormConf(), oldProcessDefinitionExt.getFormConf()) + || !ObjectUtil.equal(createReqDTO.getFormFields(), oldProcessDefinitionExt.getFormFields()) + || !ObjectUtil.equal(createReqDTO.getFormCustomCreatePath(), oldProcessDefinitionExt.getFormCustomCreatePath()) + || !ObjectUtil.equal(createReqDTO.getFormCustomViewPath(), oldProcessDefinitionExt.getFormCustomViewPath())) { + return false; + } + // 校验 BPMN XML 信息 + BpmnModel newModel = buildBpmnModel(createReqDTO.getBpmnBytes()); + BpmnModel oldModel = getBpmnModel(oldProcessDefinition.getId()); + // TODO 貌似 flowable 不修改这个也不同。需要看看。 sourceSystemId 不同 + if (!FlowableUtils.equals(oldModel, newModel)) { + return false; + } + // 最终发现都一致,则返回 true + return true; + } + + /** + * 构建对应的 BPMN Model + * + * @param bpmnBytes 原始的 BPMN XML 字节数组 + * @return BPMN Model + */ + private BpmnModel buildBpmnModel(byte[] bpmnBytes) { + // 转换成 BpmnModel 对象 + BpmnXMLConverter converter = new BpmnXMLConverter(); + return converter.convertToBpmnModel(new BytesStreamSource(bpmnBytes), true, true); + } + + @Override + public BpmProcessDefinitionExtDO getProcessDefinitionExt(String id) { + return processDefinitionMapper.selectByProcessDefinitionId(id); + } + + @Override + public List getProcessDefinitionList(BpmProcessDefinitionListReqVO listReqVO) { + // 拼接查询条件 + ProcessDefinitionQuery definitionQuery = repositoryService.createProcessDefinitionQuery(); + if (Objects.equals(SuspensionState.SUSPENDED.getStateCode(), listReqVO.getSuspensionState())) { + definitionQuery.suspended(); + } else if (Objects.equals(SuspensionState.ACTIVE.getStateCode(), listReqVO.getSuspensionState())) { + definitionQuery.active(); + } + // 执行查询 + List processDefinitions = definitionQuery.list(); + if (CollUtil.isEmpty(processDefinitions)) { + return Collections.emptyList(); + } + + // 获得 BpmProcessDefinitionDO Map + List processDefinitionDOs = processDefinitionMapper.selectListByProcessDefinitionIds( + convertList(processDefinitions, ProcessDefinition::getId)); + Map processDefinitionDOMap = convertMap(processDefinitionDOs, + BpmProcessDefinitionExtDO::getProcessDefinitionId); + // 执行查询,并返回 + return BpmProcessDefinitionConvert.INSTANCE.convertList3(processDefinitions, processDefinitionDOMap); + } + + @Override + public PageResult getProcessDefinitionPage(BpmProcessDefinitionPageReqVO pageVO) { + ProcessDefinitionQuery definitionQuery = repositoryService.createProcessDefinitionQuery(); + if (StrUtil.isNotBlank(pageVO.getKey())) { + definitionQuery.processDefinitionKey(pageVO.getKey()); + } + + // 执行查询 + List processDefinitions = definitionQuery.orderByProcessDefinitionVersion().desc() + .listPage(PageUtils.getStart(pageVO), pageVO.getPageSize()); + + if (CollUtil.isEmpty(processDefinitions)) { + return new PageResult<>(emptyList(), definitionQuery.count()); + } + // 获得 Deployment Map + Set deploymentIds = new HashSet<>(); + processDefinitions.forEach(definition -> addIfNotNull(deploymentIds, definition.getDeploymentId())); + Map deploymentMap = getDeploymentMap(deploymentIds); + + // 获得 BpmProcessDefinitionDO Map + List processDefinitionDOs = processDefinitionMapper.selectListByProcessDefinitionIds( + convertList(processDefinitions, ProcessDefinition::getId)); + Map processDefinitionDOMap = convertMap(processDefinitionDOs, + BpmProcessDefinitionExtDO::getProcessDefinitionId); + + // 获得 Form Map + Set formIds = convertSet(processDefinitionDOs, BpmProcessDefinitionExtDO::getFormId); + Map formMap = formService.getFormMap(formIds); + + // 拼接结果 + long definitionCount = definitionQuery.count(); + return new PageResult<>(BpmProcessDefinitionConvert.INSTANCE.convertList(processDefinitions, deploymentMap, + processDefinitionDOMap, formMap), definitionCount); + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/impl/BpmProcessInstanceApiImpl.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/impl/BpmProcessInstanceApiImpl.java new file mode 100644 index 00000000..b0413e14 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/impl/BpmProcessInstanceApiImpl.java @@ -0,0 +1,29 @@ +package com.ruoyi.flowable.service.definition.impl; + +import com.ruoyi.flowable.domain.dto.task.BpmProcessInstanceCreateReqDTO; +import com.ruoyi.flowable.service.definition.BpmProcessInstanceApi; +import com.ruoyi.flowable.service.task.BpmProcessInstanceService; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import javax.validation.Valid; + +/** + * Flowable 流程实例 Api 实现类 + * + * hasPermi + * @author jason + */ +@Service +@Validated +public class BpmProcessInstanceApiImpl implements BpmProcessInstanceApi { + + @Resource + private BpmProcessInstanceService processInstanceService; + + @Override + public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO reqDTO) { + return processInstanceService.createProcessInstance(userId, reqDTO); + } +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/impl/BpmTaskAssignRuleServiceImpl.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/impl/BpmTaskAssignRuleServiceImpl.java new file mode 100644 index 00000000..297e5c30 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/impl/BpmTaskAssignRuleServiceImpl.java @@ -0,0 +1,361 @@ +package com.ruoyi.flowable.service.definition.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONUtil; +import com.google.common.annotations.VisibleForTesting; +import com.ruoyi.common.enums.CommonStatusEnum; +import com.ruoyi.common.utils.ObjectUtils; +import com.ruoyi.common.utils.collection.CollectionUtils; +import com.ruoyi.flowable.convert.definition.BpmTaskAssignRuleConvert; +import com.ruoyi.flowable.core.enums.DictTypeConstants; +import com.ruoyi.flowable.core.enums.definition.BpmTaskAssignRuleTypeEnum; +import com.ruoyi.flowable.core.utils.FlowableUtils; +import com.ruoyi.flowable.domain.dto.user.AdminUserRespDTO; +import com.ruoyi.flowable.domain.dto.user.DeptRespDTO; +import com.ruoyi.flowable.domain.entity.definition.BpmTaskAssignRuleDO; +import com.ruoyi.flowable.domain.entity.definition.BpmUserGroupDO; +import com.ruoyi.flowable.domain.vo.rule.BpmTaskAssignRuleCreateReqVO; +import com.ruoyi.flowable.domain.vo.rule.BpmTaskAssignRuleRespVO; +import com.ruoyi.flowable.domain.vo.rule.BpmTaskAssignRuleUpdateReqVO; +import com.ruoyi.flowable.framework.flowable.core.behavior.script.BpmTaskAssignScript; +import com.ruoyi.flowable.mapper.definition.BpmTaskAssignRuleMapper; +import com.ruoyi.flowable.service.definition.BpmModelService; +import com.ruoyi.flowable.service.definition.BpmProcessDefinitionService; +import com.ruoyi.flowable.service.definition.BpmTaskAssignRuleService; +import com.ruoyi.flowable.service.definition.BpmUserGroupService; +import com.ruoyi.flowable.service.user.*; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.bpmn.model.UserTask; +import org.flowable.common.engine.api.FlowableException; +import org.flowable.engine.delegate.DelegateExecution; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.*; + +import static cn.hutool.core.text.CharSequenceUtil.format; +import static com.ruoyi.common.exception.util.ServiceExceptionUtil.exception; +import static com.ruoyi.common.utils.JsonUtils.toJsonString; +import static com.ruoyi.common.utils.collection.CollectionUtils.convertSet; +import static com.ruoyi.flowable.core.enums.ErrorCodeConstants.*; + +/** + * BPM 任务分配规则 Service 实现类 + */ +@Service +@Validated +@Slf4j +public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService { + + @Resource + private BpmTaskAssignRuleMapper taskRuleMapper; + @Resource + @Lazy // 解决循环依赖 + private BpmModelService modelService; + @Resource + @Lazy // 解决循环依赖 + private BpmProcessDefinitionService processDefinitionService; + + @Resource + private BpmUserGroupService userGroupService; + @Resource + private AdminUserApi adminUserApi; + @Resource + private RoleApi roleApi; + @Resource + private DeptApi deptApi; + @Resource + private PostApi postApi; + @Resource + private DictDataApi dictDataApi; + @Resource + private PermissionApi permissionApi; + + /** + * 任务分配脚本 + */ + private Map scriptMap = Collections.emptyMap(); + + + @Override + public List getTaskAssignRuleListByProcessDefinitionId(String processDefinitionId, + String taskDefinitionKey) { + return taskRuleMapper.selectListByProcessDefinitionId(processDefinitionId, taskDefinitionKey); + } + + @Override + public List getTaskAssignRuleListByModelId(String modelId) { + return taskRuleMapper.selectListByModelId(modelId); + } + + @Override + public List getTaskAssignRuleList(String modelId, String processDefinitionId) { + // 获得规则 + List rules = Collections.emptyList(); + BpmnModel model = null; + if (StrUtil.isNotEmpty(modelId)) { + rules = getTaskAssignRuleListByModelId(modelId); + model = modelService.getBpmnModel(modelId); + } else if (StrUtil.isNotEmpty(processDefinitionId)) { + rules = getTaskAssignRuleListByProcessDefinitionId(processDefinitionId, null); + model = processDefinitionService.getBpmnModel(processDefinitionId); + } + if (model == null) { + return Collections.emptyList(); + } + // 获得用户任务,只有用户任务才可以设置分配规则 + List userTasks = FlowableUtils.getBpmnModelElements(model, UserTask.class); + if (CollUtil.isEmpty(userTasks)) { + return Collections.emptyList(); + } + // 转换数据 + return BpmTaskAssignRuleConvert.INSTANCE.convertList(userTasks, rules); + } + + @Override + public Long createTaskAssignRule(@Valid BpmTaskAssignRuleCreateReqVO reqVO) { + // 校验参数 + validTaskAssignRuleOptions(reqVO.getType(), reqVO.getOptions()); + // 校验是否已经配置 + BpmTaskAssignRuleDO existRule = + taskRuleMapper.selectListByModelIdAndTaskDefinitionKey(reqVO.getModelId(), reqVO.getTaskDefinitionKey()); + if (existRule != null) { + throw exception(TASK_ASSIGN_RULE_EXISTS, reqVO.getModelId(), reqVO.getTaskDefinitionKey()); + } + + // 存储 + BpmTaskAssignRuleDO rule = BpmTaskAssignRuleConvert.INSTANCE.convert(reqVO); + // 只有流程模型,才允许新建 + rule.setProcessDefinitionId(BpmTaskAssignRuleDO.PROCESS_DEFINITION_ID_NULL); + taskRuleMapper.insert(rule); + return rule.getId(); + } + + @Override + public void updateTaskAssignRule(@Valid BpmTaskAssignRuleUpdateReqVO reqVO) { + // 校验参数 + validTaskAssignRuleOptions(reqVO.getType(), reqVO.getOptions()); + // 校验是否存在 + BpmTaskAssignRuleDO existRule = taskRuleMapper.selectById(reqVO.getId()); + if (existRule == null) { + throw exception(TASK_ASSIGN_RULE_NOT_EXISTS); + } + // 只允许修改流程模型的规则 + if (!Objects.equals(BpmTaskAssignRuleDO.PROCESS_DEFINITION_ID_NULL, existRule.getProcessDefinitionId())) { + throw exception(TASK_UPDATE_FAIL_NOT_MODEL); + } + + // 执行更新 + taskRuleMapper.updateById(BpmTaskAssignRuleConvert.INSTANCE.convert(reqVO)); + } + + @Override + public boolean isTaskAssignRulesEquals(String modelId, String processDefinitionId) { + // 调用 VO 接口的原因是,过滤掉流程模型不需要的规则,保持和 copyTaskAssignRules 方法的一致性 + List modelRules = getTaskAssignRuleList(modelId, null); + List processInstanceRules = getTaskAssignRuleList(null, processDefinitionId); + if (modelRules.size() != processInstanceRules.size()) { + return false; + } + + // 遍历,匹配对应的规则 + Map processInstanceRuleMap = + CollectionUtils.convertMap(processInstanceRules, BpmTaskAssignRuleRespVO::getTaskDefinitionKey); + for (BpmTaskAssignRuleRespVO modelRule : modelRules) { + BpmTaskAssignRuleRespVO processInstanceRule = processInstanceRuleMap.get(modelRule.getTaskDefinitionKey()); + if (processInstanceRule == null) { + return false; + } + if (!ObjectUtil.equals(modelRule.getType(), processInstanceRule.getType()) || !ObjectUtil.equal( + modelRule.getOptions(), processInstanceRule.getOptions())) { + return false; + } + } + return true; + } + + @Override + public void copyTaskAssignRules(String fromModelId, String toProcessDefinitionId) { + List rules = getTaskAssignRuleList(fromModelId, null); + if (CollUtil.isEmpty(rules)) { + return; + } + // 开始复制 + List newRules = BpmTaskAssignRuleConvert.INSTANCE.convertList2(rules); + newRules.forEach(rule -> { + rule.setProcessDefinitionId(toProcessDefinitionId); + rule.setId(null); + rule.setCreateTime(null); + rule.setUpdateTime(null); + }); + taskRuleMapper.insertBatch(newRules); + } + + @Override + public void checkTaskAssignRuleAllConfig(String id) { + // 一个用户任务都没配置,所以无需配置规则 + List taskAssignRules = getTaskAssignRuleList(id, null); + if (CollUtil.isEmpty(taskAssignRules)) { + return; + } + // 校验未配置规则的任务 + taskAssignRules.forEach(rule -> { + if (CollUtil.isEmpty(rule.getOptions()) && !rule.getType().equals(60)) { + throw exception(MODEL_DEPLOY_FAIL_TASK_ASSIGN_RULE_NOT_CONFIG, rule.getTaskDefinitionName()); + } + }); + } + + private void validTaskAssignRuleOptions(Integer type, Set options) { + if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.ROLE.getType())) { + roleApi.validRoles(options); + } else if (ObjectUtils.equalsAny(type, BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(), + BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType())) { + deptApi.validDepts(options); + } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.POST.getType())) { + postApi.validPosts(options); + } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.USER.getType())) { + adminUserApi.validUsers(options); + } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.USER_GROUP.getType())) { + userGroupService.validUserGroups(options); + } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.SCRIPT.getType())) { + dictDataApi.validDictDatas(DictTypeConstants.TASK_ASSIGN_SCRIPT, CollectionUtils.convertSet(options, String::valueOf)); + } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.VARIABLE.getType())) { + //流程变量。 + } else { + throw new IllegalArgumentException(format("未知的规则类型({})", type)); + } + } + + @Override + //@DataPermission(enable = false) // 忽略数据权限,不然分配会存在问题 + public Set calculateTaskCandidateUsers(DelegateExecution execution) { + BpmTaskAssignRuleDO rule = getTaskRule(execution); + return calculateTaskCandidateUsers(execution, rule); + } + + @VisibleForTesting + BpmTaskAssignRuleDO getTaskRule(DelegateExecution execution) { + List taskRules = getTaskAssignRuleListByProcessDefinitionId( + execution.getProcessDefinitionId(), execution.getCurrentActivityId()); + if (CollUtil.isEmpty(taskRules)) { + throw new FlowableException(format("流程任务({}/{}/{}) 找不到符合的任务规则", + execution.getId(), execution.getProcessDefinitionId(), execution.getCurrentActivityId())); + } + if (taskRules.size() > 1) { + throw new FlowableException(format("流程任务({}/{}/{}) 找到过多任务规则({})", + execution.getId(), execution.getProcessDefinitionId(), execution.getCurrentActivityId())); + } + return taskRules.get(0); + } + + @VisibleForTesting + Set calculateTaskCandidateUsers(DelegateExecution execution, BpmTaskAssignRuleDO rule) { + Set assigneeUserIds = null; + if (Objects.equals(BpmTaskAssignRuleTypeEnum.ROLE.getType(), rule.getType())) { + assigneeUserIds = calculateTaskCandidateUsersByRole(rule); + } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(), rule.getType())) { + assigneeUserIds = calculateTaskCandidateUsersByDeptMember(rule); + } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType(), rule.getType())) { + assigneeUserIds = calculateTaskCandidateUsersByDeptLeader(rule); + } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.POST.getType(), rule.getType())) { + assigneeUserIds = calculateTaskCandidateUsersByPost(rule); + } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER.getType(), rule.getType())) { + assigneeUserIds = calculateTaskCandidateUsersByUser(rule); + } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.USER_GROUP.getType(), rule.getType())) { + assigneeUserIds = calculateTaskCandidateUsersByUserGroup(rule); + } else if (Objects.equals(BpmTaskAssignRuleTypeEnum.SCRIPT.getType(), rule.getType())) { + assigneeUserIds = calculateTaskCandidateUsersByScript(execution, rule); + }else if (Objects.equals(BpmTaskAssignRuleTypeEnum.VARIABLE.getType(), rule.getType())) { + assigneeUserIds = calculateTaskCandidateUsersByVariable(execution, rule); + } + + // 移除被禁用的用户 + removeDisableUsers(assigneeUserIds); + // 如果候选人为空,抛出异常 + if (CollUtil.isEmpty(assigneeUserIds)) { + log.error("[calculateTaskCandidateUsers][流程任务({}/{}/{}) 任务规则({}) 找不到候选人]", execution.getId(), + execution.getProcessDefinitionId(), execution.getCurrentActivityId(), toJsonString(rule)); + throw exception(TASK_CREATE_FAIL_NO_CANDIDATE_USER); + } + return assigneeUserIds; + } + + private Set calculateTaskCandidateUsersByRole(BpmTaskAssignRuleDO rule) { + return permissionApi.getUserRoleIdListByRoleIds(rule.getOptions()); + } + + private Set calculateTaskCandidateUsersByDeptMember(BpmTaskAssignRuleDO rule) { + List users = adminUserApi.getUsersByDeptIds(rule.getOptions()); + return convertSet(users, AdminUserRespDTO::getId); + } + + private Set calculateTaskCandidateUsersByDeptLeader(BpmTaskAssignRuleDO rule) { + List depts = deptApi.getDepts(rule.getOptions()); + return convertSet(depts, DeptRespDTO::getLeaderUserId); + } + + private Set calculateTaskCandidateUsersByPost(BpmTaskAssignRuleDO rule) { + List users = adminUserApi.getUsersByPostIds(rule.getOptions()); + return convertSet(users, AdminUserRespDTO::getId); + } + + private Set calculateTaskCandidateUsersByUser(BpmTaskAssignRuleDO rule) { + return rule.getOptions(); + } + + private Set calculateTaskCandidateUsersByUserGroup(BpmTaskAssignRuleDO rule) { + List userGroups = userGroupService.getUserGroupList(rule.getOptions()); + Set userIds = new HashSet<>(); + userGroups.forEach(group -> userIds.addAll(group.getMemberUserIds())); + return userIds; + } + + private Set calculateTaskCandidateUsersByScript(DelegateExecution execution, BpmTaskAssignRuleDO rule) { + // 获得对应的脚本 + List scripts = new ArrayList<>(rule.getOptions().size()); + rule.getOptions().forEach(id -> { + BpmTaskAssignScript script = scriptMap.get(id); + if (script == null) { + throw exception(TASK_ASSIGN_SCRIPT_NOT_EXISTS, id); + } + scripts.add(script); + }); + // 逐个计算任务 + Set userIds = new HashSet<>(); + scripts.forEach(script -> CollUtil.addAll(userIds, script.calculateTaskCandidateUsers(execution))); + return userIds; + } + private Set calculateTaskCandidateUsersByVariable(DelegateExecution execution, BpmTaskAssignRuleDO rule) { + // 获得对应的脚本 + Object variable = execution.getVariable(FlowableUtils.formatCollectionVariable(execution.getCurrentActivityId())); + if (variable == null || StrUtil.isBlank(variable.toString())) { + return new HashSet<>(); + } + JSONArray jsonArray = JSONUtil.parseArray(variable.toString()); + Set ids = new HashSet<>(); + for (int i = 0; i < jsonArray.size(); i++) { + ids.add(Long.valueOf(jsonArray.get(i).toString())); + } + return ids; + } + @VisibleForTesting + void removeDisableUsers(Set assigneeUserIds) { + if (CollUtil.isEmpty(assigneeUserIds)) { + return; + } + Map userMap = adminUserApi.getUserMap(assigneeUserIds); + assigneeUserIds.removeIf(id -> { + AdminUserRespDTO user = userMap.get(id); + return user == null || !CommonStatusEnum.ENABLE.getStatus().equals(user.getStatus()); + }); + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/impl/BpmUserGroupServiceImpl.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/impl/BpmUserGroupServiceImpl.java new file mode 100644 index 00000000..15716203 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/definition/impl/BpmUserGroupServiceImpl.java @@ -0,0 +1,113 @@ +package com.ruoyi.flowable.service.definition.impl; + +import cn.hutool.core.collection.CollUtil; +import com.ruoyi.common.enums.CommonStatusEnum; +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.common.utils.collection.CollectionUtils; +import com.ruoyi.flowable.convert.definition.BpmUserGroupConvert; +import com.ruoyi.flowable.domain.entity.definition.BpmUserGroupDO; +import com.ruoyi.flowable.domain.vo.group.BpmUserGroupCreateReqVO; +import com.ruoyi.flowable.domain.vo.group.BpmUserGroupPageReqVO; +import com.ruoyi.flowable.domain.vo.group.BpmUserGroupUpdateReqVO; +import com.ruoyi.flowable.mapper.definition.BpmUserGroupMapper; +import com.ruoyi.flowable.service.definition.BpmUserGroupService; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static com.ruoyi.common.exception.util.ServiceExceptionUtil.exception; +import static com.ruoyi.flowable.core.enums.ErrorCodeConstants.USER_GROUP_IS_DISABLE; +import static com.ruoyi.flowable.core.enums.ErrorCodeConstants.USER_GROUP_NOT_EXISTS; + + +/** + * 用户组 Service 实现类 + *

+ * hasPermi + */ +@Service +@Validated +public class BpmUserGroupServiceImpl implements BpmUserGroupService { + + @Resource + private BpmUserGroupMapper userGroupMapper; + + @Override + public Long createUserGroup(BpmUserGroupCreateReqVO createReqVO) { + // 插入 + BpmUserGroupDO userGroup = BpmUserGroupConvert.INSTANCE.convert(createReqVO); + userGroupMapper.insert(userGroup); + // 返回 + return userGroup.getId(); + } + + @Override + public void updateUserGroup(BpmUserGroupUpdateReqVO updateReqVO) { + // 校验存在 + this.validateUserGroupExists(updateReqVO.getId()); + // 更新 + BpmUserGroupDO updateObj = BpmUserGroupConvert.INSTANCE.convert(updateReqVO); + userGroupMapper.updateById(updateObj); + } + + @Override + public void deleteUserGroup(Long id) { + // 校验存在 + this.validateUserGroupExists(id); + // 删除 + userGroupMapper.deleteById(id); + } + + private void validateUserGroupExists(Long id) { + if (userGroupMapper.selectById(id) == null) { + throw exception(USER_GROUP_NOT_EXISTS); + } + } + + @Override + public BpmUserGroupDO getUserGroup(Long id) { + return userGroupMapper.selectById(id); + } + + @Override + public List getUserGroupList(Collection ids) { + return userGroupMapper.selectBatchIds(ids); + } + + + @Override + public List getUserGroupListByStatus(Integer status) { + return userGroupMapper.selectListByStatus(status); + } + + @Override + public PageResult getUserGroupPage(BpmUserGroupPageReqVO pageReqVO) { + return userGroupMapper.selectPage(pageReqVO); + } + + @Override + public void validUserGroups(Set ids) { + if (CollUtil.isEmpty(ids)) { + return; + } + // 获得用户组信息 + List userGroups = userGroupMapper.selectBatchIds(ids); + Map userGroupMap = CollectionUtils.convertMap(userGroups, BpmUserGroupDO::getId); + // 校验 + ids.forEach(id -> { + BpmUserGroupDO userGroup = userGroupMap.get(id); + if (userGroup == null) { + throw exception(USER_GROUP_NOT_EXISTS); + } + if (!CommonStatusEnum.ENABLE.getStatus().equals(userGroup.getStatus())) { + throw exception(USER_GROUP_IS_DISABLE, userGroup.getName()); + } + }); + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/message/BpmMessageService.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/message/BpmMessageService.java new file mode 100644 index 00000000..f1767dab --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/message/BpmMessageService.java @@ -0,0 +1,40 @@ +package com.ruoyi.flowable.service.message; + + +import com.ruoyi.flowable.domain.dto.message.BpmMessageSendWhenProcessInstanceApproveReqDTO; +import com.ruoyi.flowable.domain.dto.message.BpmMessageSendWhenProcessInstanceRejectReqDTO; +import com.ruoyi.flowable.domain.dto.message.BpmMessageSendWhenTaskCreatedReqDTO; + +import javax.validation.Valid; + +/** + * BPM 消息 Service 接口 + * + * TODO 芋艿:未来支持消息的可配置;不同的流程,在什么场景下,需要发送什么消息,消息的内容是什么; + * + * hasPermi + */ +public interface BpmMessageService { + + /** + * 发送流程实例被通过的消息 + * + * @param reqDTO 发送信息 + */ + void sendMessageWhenProcessInstanceApprove(@Valid BpmMessageSendWhenProcessInstanceApproveReqDTO reqDTO); + + /** + * 发送流程实例被不通过的消息 + * + * @param reqDTO 发送信息 + */ + void sendMessageWhenProcessInstanceReject(@Valid BpmMessageSendWhenProcessInstanceRejectReqDTO reqDTO); + + /** + * 发送任务被分配的消息 + * + * @param reqDTO 发送信息 + */ + void sendMessageWhenTaskAssigned(@Valid BpmMessageSendWhenTaskCreatedReqDTO reqDTO); + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/message/impl/BpmMessageServiceImpl.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/message/impl/BpmMessageServiceImpl.java new file mode 100644 index 00000000..449974a8 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/message/impl/BpmMessageServiceImpl.java @@ -0,0 +1,69 @@ +package com.ruoyi.flowable.service.message.impl; + +import com.ruoyi.flowable.convert.message.BpmMessageConvert; +import com.ruoyi.flowable.core.enums.message.BpmMessageEnum; +import com.ruoyi.flowable.domain.dto.message.BpmMessageSendWhenProcessInstanceApproveReqDTO; +import com.ruoyi.flowable.domain.dto.message.BpmMessageSendWhenProcessInstanceRejectReqDTO; +import com.ruoyi.flowable.domain.dto.message.BpmMessageSendWhenTaskCreatedReqDTO; +import com.ruoyi.flowable.service.message.BpmMessageService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.web.WebProperties; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.Map; + +/** + * BPM 消息 Service 实现类 + * + * hasPermi + */ +@Service +@Validated +@Slf4j +public class BpmMessageServiceImpl implements BpmMessageService { + + //@Resource + //private SmsSendApi smsSendApi; + + @Resource + private WebProperties webProperties; + + @Override + public void sendMessageWhenProcessInstanceApprove(BpmMessageSendWhenProcessInstanceApproveReqDTO reqDTO) { + Map templateParams = new HashMap<>(); + templateParams.put("processInstanceName", reqDTO.getProcessInstanceName()); + templateParams.put("detailUrl", getProcessInstanceDetailUrl(reqDTO.getProcessInstanceId())); + //smsSendApi.sendSingleSmsToAdmin(BpmMessageConvert.INSTANCE.convert(reqDTO.getStartUserId(), + // BpmMessageEnum.PROCESS_INSTANCE_APPROVE.getSmsTemplateCode(), templateParams)); + } + + @Override + public void sendMessageWhenProcessInstanceReject(BpmMessageSendWhenProcessInstanceRejectReqDTO reqDTO) { + Map templateParams = new HashMap<>(); + templateParams.put("processInstanceName", reqDTO.getProcessInstanceName()); + templateParams.put("reason", reqDTO.getReason()); + templateParams.put("detailUrl", getProcessInstanceDetailUrl(reqDTO.getProcessInstanceId())); + //smsSendApi.sendSingleSmsToAdmin(BpmMessageConvert.INSTANCE.convert(reqDTO.getStartUserId(), + // BpmMessageEnum.PROCESS_INSTANCE_REJECT.getSmsTemplateCode(), templateParams)); + } + + @Override + public void sendMessageWhenTaskAssigned(BpmMessageSendWhenTaskCreatedReqDTO reqDTO) { + Map templateParams = new HashMap<>(); + templateParams.put("processInstanceName", reqDTO.getProcessInstanceName()); + templateParams.put("taskName", reqDTO.getTaskName()); + templateParams.put("startUserNickname", reqDTO.getStartUserNickname()); + templateParams.put("detailUrl", getProcessInstanceDetailUrl(reqDTO.getProcessInstanceId())); + //smsSendApi.sendSingleSmsToAdmin(BpmMessageConvert.INSTANCE.convert(reqDTO.getStartUserId(), + // BpmMessageEnum.TASK_ASSIGNED.getSmsTemplateCode(), templateParams)); + } + + private String getProcessInstanceDetailUrl(String taskId) { + //return webProperties.getAdminUi().getUrl() + "/bpm/process-instance/detail?id=" + taskId; + return null; + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/oa/BpmOALeaveService.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/oa/BpmOALeaveService.java new file mode 100644 index 00000000..7f3c7b45 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/oa/BpmOALeaveService.java @@ -0,0 +1,59 @@ +package com.ruoyi.flowable.service.oa; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.flowable.domain.entity.oa.BpmOALeaveDO; +import com.ruoyi.flowable.domain.vo.oa.BpmOALeaveCreateReqVO; +import com.ruoyi.flowable.domain.vo.oa.BpmOALeavePageReqVO; + +import javax.validation.Valid; + +/** + * 请假申请 Service 接口 + * + * @author jason + * hasPermi + */ +public interface BpmOALeaveService { + + /** + * 创建请假申请 + * + * @param userId 用户编号 + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createLeave(Long userId, @Valid BpmOALeaveCreateReqVO createReqVO); + + /** + * 更新请假申请的状态 + * + * @param id 编号 + * @param result 结果 + */ + void updateLeaveResult(Long id, Integer result); + + /** + * 获得请假申请 + * + * @param id 编号 + * @return 请假申请 + */ + BpmOALeaveDO getLeave(Long id); + + /** + * 获得请假申请分页 + * + * @param userId 用户编号 + * @param pageReqVO 分页查询 + * @return 请假申请分页 + */ + PageResult getLeavePage(Long userId, BpmOALeavePageReqVO pageReqVO); + /** + * 分页查询OA 请假申请列表 + * + * @param bpmOaLeave OA 请假申请 + * @return OA 请假申请集合 + */ + public IPage selectList(IPage page, BpmOALeaveDO bpmOaLeave); +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/oa/BpmOALeaveServiceImpl.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/oa/BpmOALeaveServiceImpl.java new file mode 100644 index 00000000..8b47612b --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/oa/BpmOALeaveServiceImpl.java @@ -0,0 +1,99 @@ +package com.ruoyi.flowable.service.oa; + +import cn.hutool.core.date.DateUtil; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.flowable.convert.oa.BpmOALeaveConvert; +import com.ruoyi.flowable.core.enums.task.BpmProcessInstanceResultEnum; +import com.ruoyi.flowable.domain.dto.task.BpmProcessInstanceCreateReqDTO; +import com.ruoyi.flowable.domain.entity.oa.BpmOALeaveDO; +import com.ruoyi.flowable.domain.vo.oa.BpmOALeaveCreateReqVO; +import com.ruoyi.flowable.domain.vo.oa.BpmOALeavePageReqVO; +import com.ruoyi.flowable.mapper.oa.BpmOALeaveMapper; +import com.ruoyi.flowable.service.definition.BpmProcessInstanceApi; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.Map; + +import static com.ruoyi.common.exception.util.ServiceExceptionUtil.exception; +import static com.ruoyi.flowable.core.enums.ErrorCodeConstants.OA_LEAVE_NOT_EXISTS; + +/** + * OA 请假申请 Service 实现类 + * + * @author jason + * hasPermi + */ +@Service +@Validated +public class BpmOALeaveServiceImpl implements BpmOALeaveService { + + /** + * OA 请假对应的流程定义 KEY + */ + public static final String PROCESS_KEY = "oa_leave"; + + @Resource + private BpmOALeaveMapper leaveMapper; + + @Resource + private BpmProcessInstanceApi processInstanceApi; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createLeave(Long userId, BpmOALeaveCreateReqVO createReqVO) { + // 插入 OA 请假单 + long day = DateUtil.betweenDay(createReqVO.getStartTime(), createReqVO.getEndTime(), false); + BpmOALeaveDO leave = BpmOALeaveConvert.INSTANCE.convert(createReqVO).setUserId(userId).setDay(day) + .setResult(BpmProcessInstanceResultEnum.PROCESS.getResult()); + leaveMapper.insert(leave); + + // 发起 BPM 流程 + Map processInstanceVariables = new HashMap<>(); + processInstanceVariables.put("day", day); + String processInstanceId = processInstanceApi.createProcessInstance(userId, + new BpmProcessInstanceCreateReqDTO().setProcessDefinitionKey(PROCESS_KEY) + .setVariables(processInstanceVariables).setBusinessKey(String.valueOf(leave.getId()))); + + // 将工作流的编号,更新到 OA 请假单中 + leaveMapper.updateById(new BpmOALeaveDO().setId(leave.getId()).setProcessInstanceId(processInstanceId)); + return leave.getId(); + } + + @Override + public void updateLeaveResult(Long id, Integer result) { + validateLeaveExists(id); + leaveMapper.updateById(new BpmOALeaveDO().setId(id).setResult(result)); + } + + private void validateLeaveExists(Long id) { + if (leaveMapper.selectById(id) == null) { + throw exception(OA_LEAVE_NOT_EXISTS); + } + } + + @Override + public BpmOALeaveDO getLeave(Long id) { + return leaveMapper.selectById(id); + } + + @Override + public PageResult getLeavePage(Long userId, BpmOALeavePageReqVO pageReqVO) { + return leaveMapper.selectPage(userId, pageReqVO); + } + + /** + * 分页查询OA 请假申请列表 + * + * @param bpmOaLeave OA 请假申请 + * @return OA 请假申请 + */ + @Override + public IPage selectList(IPage page, BpmOALeaveDO bpmOaLeave) { + return leaveMapper.selectBpmOaLeavePage(page, bpmOaLeave); + } +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/oa/listener/BpmOALeaveResultListener.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/oa/listener/BpmOALeaveResultListener.java new file mode 100644 index 00000000..8723d233 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/oa/listener/BpmOALeaveResultListener.java @@ -0,0 +1,32 @@ +package com.ruoyi.flowable.service.oa.listener; + +import com.ruoyi.flowable.framework.bpm.core.event.BpmProcessInstanceResultEvent; +import com.ruoyi.flowable.framework.bpm.core.event.BpmProcessInstanceResultEventListener; +import com.ruoyi.flowable.service.oa.BpmOALeaveService; +import com.ruoyi.flowable.service.oa.BpmOALeaveServiceImpl; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +/** + * OA 请假单的结果的监听器实现类 + * + * hasPermi + */ +@Component +public class BpmOALeaveResultListener extends BpmProcessInstanceResultEventListener { + + @Resource + private BpmOALeaveService leaveService; + + @Override + protected String getProcessDefinitionKey() { + return BpmOALeaveServiceImpl.PROCESS_KEY; + } + + @Override + protected void onEvent(BpmProcessInstanceResultEvent event) { + leaveService.updateLeaveResult(Long.parseLong(event.getBusinessKey()), event.getResult()); + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/task/BpmActivityService.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/task/BpmActivityService.java new file mode 100644 index 00000000..39d01a5a --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/task/BpmActivityService.java @@ -0,0 +1,31 @@ +package com.ruoyi.flowable.service.task; + +import com.ruoyi.flowable.domain.vo.activity.BpmActivityRespVO; +import org.flowable.engine.history.HistoricActivityInstance; + +import java.util.List; + +/** + * BPM 活动实例 Service 接口 + * + * hasPermi + */ +public interface BpmActivityService { + + /** + * 获得指定流程实例的活动实例列表 + * + * @param processInstanceId 流程实例的编号 + * @return 活动实例列表 + */ + List getActivityListByProcessInstanceId(String processInstanceId); + + /** + * 获得执行编号对应的活动实例 + * + * @param executionId 执行编号 + * @return 活动实例 + */ + List getHistoricActivityListByExecutionId(String executionId); + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/task/BpmProcessInstanceService.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/task/BpmProcessInstanceService.java new file mode 100644 index 00000000..531e729d --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/task/BpmProcessInstanceService.java @@ -0,0 +1,147 @@ +package com.ruoyi.flowable.service.task; + +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.common.utils.collection.CollectionUtils; +import com.ruoyi.flowable.domain.dto.task.BpmProcessInstanceCreateReqDTO; +import com.ruoyi.flowable.domain.vo.instance.*; +import org.flowable.engine.delegate.event.FlowableCancelledEvent; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.runtime.ProcessInstance; + +import javax.validation.Valid; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 流程实例 Service 接口 + * + * hasPermi + */ +public interface BpmProcessInstanceService { + + /** + * 获得流程实例 + * + * @param id 流程实例的编号 + * @return 流程实例 + */ + ProcessInstance getProcessInstance(String id); + + /** + * 获得流程实例列表 + * + * @param ids 流程实例的编号集合 + * @return 流程实例列表 + */ + List getProcessInstances(Set ids); + + /** + * 获得流程实例 Map + * + * @param ids 流程实例的编号集合 + * @return 流程实例列表 Map + */ + default Map getProcessInstanceMap(Set ids) { + return CollectionUtils.convertMap(getProcessInstances(ids), ProcessInstance::getProcessInstanceId); + } + + /** + * 获得流程实例的分页 + * + * @param userId 用户编号 + * @param pageReqVO 分页请求 + * @return 流程实例的分页 + */ + PageResult getMyProcessInstancePage(Long userId, + @Valid BpmProcessInstanceMyPageReqVO pageReqVO); + /** + * 创建流程实例(提供给前端) + * + * @param userId 用户编号 + * @param createReqVO 创建信息 + * @return 实例的编号 + */ + String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO); + + /** + * 创建流程实例(提供给内部) + * + * @param userId 用户编号 + * @param createReqDTO 创建信息 + * @return 实例的编号 + */ + String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO); + + /** + * 获得流程实例 VO 信息 + * + * @param id 流程实例的编号 + * @return 流程实例 + */ + BpmProcessInstanceRespVO getProcessInstanceVO(String id); + + /** + * 取消流程实例 + * + * @param userId 用户编号 + * @param cancelReqVO 取消信息 + */ + void cancelProcessInstance(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO); + + /** + * 获得历史的流程实例 + * + * @param id 流程实例的编号 + * @return 历史的流程实例 + */ + HistoricProcessInstance getHistoricProcessInstance(String id); + + /** + * 获得历史的流程实例列表 + * + * @param ids 流程实例的编号集合 + * @return 历史的流程实例列表 + */ + List getHistoricProcessInstances(Set ids); + + /** + * 获得历史的流程实例 Map + * + * @param ids 流程实例的编号集合 + * @return 历史的流程实例列表 Map + */ + default Map getHistoricProcessInstanceMap(Set ids) { + return CollectionUtils.convertMap(getHistoricProcessInstances(ids), HistoricProcessInstance::getId); + } + + /** + * 创建 ProcessInstance 拓展记录 + * + * @param instance 流程任务 + */ + void createProcessInstanceExt(ProcessInstance instance); + + /** + * 更新 ProcessInstance 拓展记录为取消 + * + * @param event 流程取消事件 + */ + void updateProcessInstanceExtCancel(FlowableCancelledEvent event); + + /** + * 更新 ProcessInstance 拓展记录为完成 + * + * @param instance 流程任务 + */ + void updateProcessInstanceExtComplete(ProcessInstance instance); + + /** + * 更新 ProcessInstance 拓展记录为不通过 + * + * @param id 流程编号 + * @param reason 理由。例如说,审批不通过时,需要传递该值 + */ + void updateProcessInstanceExtReject(String id, String reason); + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/task/BpmTaskService.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/task/BpmTaskService.java new file mode 100644 index 00000000..c8f750c4 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/task/BpmTaskService.java @@ -0,0 +1,147 @@ +package com.ruoyi.flowable.service.task; + +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.common.utils.collection.CollectionUtils; +import com.ruoyi.flowable.domain.vo.task.*; +import org.flowable.task.api.Task; + +import javax.validation.Valid; +import java.util.List; +import java.util.Map; + +/** + * 流程任务实例 Service 接口 + * + * @author jason + * hasPermi + */ +public interface BpmTaskService { + + /** + * 获得待办的流程任务分页 + * + * @param userId 用户编号 + * @param pageReqVO 分页请求 + * + * @return 流程任务分页 + */ + PageResult getTodoTaskPage(Long userId, BpmTaskTodoPageReqVO pageReqVO); + + /** + * 获得已办的流程任务分页 + * + * @param userId 用户编号 + * @param pageReqVO 分页请求 + * + * @return 流程任务分页 + */ + PageResult getDoneTaskPage(Long userId, BpmTaskDonePageReqVO pageReqVO); + + /** + * 获得流程任务 Map + * + * @param processInstanceIds 流程实例的编号数组 + * + * @return 流程任务 Map + */ + default Map> getTaskMapByProcessInstanceIds(List processInstanceIds) { + return CollectionUtils.convertMultiMap(getTasksByProcessInstanceIds(processInstanceIds), + Task::getProcessInstanceId); + } + + /** + * 获得流程任务列表 + * + * @param processInstanceIds 流程实例的编号数组 + * + * @return 流程任务列表 + */ + List getTasksByProcessInstanceIds(List processInstanceIds); + + /** + * 获得指令流程实例的流程任务列表,包括所有状态的 + * + * @param processInstanceId 流程实例的编号 + * + * @return 流程任务列表 + */ + List getTaskListByProcessInstanceId(String processInstanceId); + + /** + * 通过任务 + * + * @param userId 用户编号 + * @param reqVO 通过请求 + */ + void approveTask(Long userId, @Valid BpmTaskApproveReqVO reqVO); + + /** + * 不通过任务 + * + * @param userId 用户编号 + * @param reqVO 不通过请求 + */ + void rejectTask(Long userId, @Valid BpmTaskRejectReqVO reqVO); + + /** + * 将流程任务分配给指定用户 + * + * @param userId 用户编号 + * @param reqVO 分配请求 + */ + void updateTaskAssignee(Long userId, BpmTaskUpdateAssigneeReqVO reqVO); + + /** + * 将流程任务分配给指定用户 + * + * @param id 流程任务编号 + * @param userId 用户编号 + */ + void updateTaskAssignee(String id, Long userId); + + /** + * 创建 Task 拓展记录 + * + * @param task 任务实体 + */ + void createTaskExt(Task task); + + /** + * 更新 Task 拓展记录为完成 + * + * @param task 任务实体 + */ + void updateTaskExtComplete(Task task); + + /** + * 更新 Task 拓展记录为已取消 + * + * @param taskId 任务的编号 + */ + void updateTaskExtCancel(String taskId); + + /** + * 更新 Task 拓展记录,并发送通知 + * + * @param task 任务实体 + */ + void updateTaskExtAssign(Task task); + + + /** + * 获取可驳回节点列表 + * @param taskId 任务id + * @param processInstanceId 流程实例id + * @return + */ + public List getBackNodesByProcessInstanceId(String processInstanceId,String taskId) ; + + /** + * 驳回任意节点 暂时没有考虑子流程 + * + * @param backTaskVo 参数 + * @return + */ + public Boolean backToStepTask(BackTaskVo backTaskVo); + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/task/impl/BpmActivityServiceImpl.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/task/impl/BpmActivityServiceImpl.java new file mode 100644 index 00000000..6472c416 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/task/impl/BpmActivityServiceImpl.java @@ -0,0 +1,41 @@ +package com.ruoyi.flowable.service.task.impl; + +import com.ruoyi.flowable.convert.task.BpmActivityConvert; +import com.ruoyi.flowable.domain.vo.activity.BpmActivityRespVO; +import com.ruoyi.flowable.service.task.BpmActivityService; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.HistoryService; +import org.flowable.engine.history.HistoricActivityInstance; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.util.List; + + +/** + * BPM 活动实例 Service 实现类 + * + * hasPermi + */ +@Service +@Slf4j +@Validated +public class BpmActivityServiceImpl implements BpmActivityService { + + @Resource + private HistoryService historyService; + + @Override + public List getActivityListByProcessInstanceId(String processInstanceId) { + List activityList = historyService.createHistoricActivityInstanceQuery() + .processInstanceId(processInstanceId).list(); + return BpmActivityConvert.INSTANCE.convertList(activityList); + } + + @Override + public List getHistoricActivityListByExecutionId(String executionId) { + return historyService.createHistoricActivityInstanceQuery().executionId(executionId).list(); + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/task/impl/BpmProcessInstanceServiceImpl.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/task/impl/BpmProcessInstanceServiceImpl.java new file mode 100644 index 00000000..e22cc9b2 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/task/impl/BpmProcessInstanceServiceImpl.java @@ -0,0 +1 @@ +package com.ruoyi.flowable.service.task.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; import com.ruoyi.common.mybatis.pojo.PageResult; import com.ruoyi.common.utils.NumberUtils; import com.ruoyi.flowable.convert.task.BpmProcessInstanceConvert; import com.ruoyi.flowable.core.enums.task.BpmProcessInstanceDeleteReasonEnum; import com.ruoyi.flowable.core.enums.task.BpmProcessInstanceResultEnum; import com.ruoyi.flowable.core.enums.task.BpmProcessInstanceStatusEnum; import com.ruoyi.flowable.domain.dto.task.BpmProcessInstanceCreateReqDTO; import com.ruoyi.flowable.domain.dto.user.AdminUserRespDTO; import com.ruoyi.flowable.domain.dto.user.DeptRespDTO; import com.ruoyi.flowable.domain.entity.definition.BpmProcessDefinitionExtDO; import com.ruoyi.flowable.domain.entity.task.BpmProcessInstanceExtDO; import com.ruoyi.flowable.domain.vo.instance.*; import com.ruoyi.flowable.framework.bpm.core.event.BpmProcessInstanceResultEventPublisher; import com.ruoyi.flowable.mapper.task.BpmProcessInstanceExtMapper; import com.ruoyi.flowable.service.definition.BpmProcessDefinitionService; import com.ruoyi.flowable.service.message.BpmMessageService; import com.ruoyi.flowable.service.task.BpmProcessInstanceService; import com.ruoyi.flowable.service.task.BpmTaskService; import com.ruoyi.flowable.service.user.AdminUserApi; import com.ruoyi.flowable.service.user.DeptApi; import com.ruoyi.plugin.tenant.core.context.TenantContextHolder; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.HistoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.delegate.event.FlowableCancelledEvent; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.engine.runtime.ProcessInstanceBuilder; import org.flowable.task.api.Task; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; import javax.validation.Valid; import java.util.*; import static com.ruoyi.common.exception.util.ServiceExceptionUtil.exception; import static com.ruoyi.common.utils.collection.CollectionUtils.convertList; import static com.ruoyi.flowable.core.enums.ErrorCodeConstants.*; /** * 流程实例 Service 实现类 *

* ProcessDefinition & ProcessInstance & Execution & Task 的关系: * 1. *

* HistoricProcessInstance & ProcessInstance 的关系: * 1. *

* 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 *

* hasPermi */ @Service @Validated @Slf4j public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService { @Resource private RuntimeService runtimeService; @Resource private BpmProcessInstanceExtMapper processInstanceExtMapper; @Resource @Lazy // 解决循环依赖 private BpmTaskService taskService; @Resource private BpmProcessDefinitionService processDefinitionService; @Resource private HistoryService historyService; @Resource private AdminUserApi adminUserApi; @Resource private DeptApi deptApi; @Resource private BpmProcessInstanceResultEventPublisher processInstanceResultEventPublisher; @Resource private BpmMessageService messageService; @Override public ProcessInstance getProcessInstance(String id) { return runtimeService.createProcessInstanceQuery().processInstanceId(id).processInstanceTenantId(TenantContextHolder.getTenantId().toString()).singleResult(); } @Override public List getProcessInstances(Set ids) { return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).processInstanceTenantId(TenantContextHolder.getTenantId().toString()).list(); } @Override public PageResult getMyProcessInstancePage(Long userId, BpmProcessInstanceMyPageReqVO pageReqVO) { // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页 PageResult pageResult = processInstanceExtMapper.selectPage(userId, pageReqVO); if (CollUtil.isEmpty(pageResult.getList())) { return new PageResult<>(pageResult.getTotal()); } // 获得流程 Task Map List processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId); Map> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds); // 转换返回 return BpmProcessInstanceConvert.INSTANCE.convertPage(pageResult, taskMap); } @Override @Transactional(rollbackFor = Exception.class) public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId()); // 发起流程 return createProcessInstance0(userId, definition, createReqVO.getVariables(), null); } @Override public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey()); // 发起流程 return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey()); } @Override public BpmProcessInstanceRespVO getProcessInstanceVO(String id) { // 获得流程实例 HistoricProcessInstance processInstance = getHistoricProcessInstance(id); if (processInstance == null) { return null; } BpmProcessInstanceExtDO processInstanceExt = processInstanceExtMapper.selectByProcessInstanceId(id); Assert.notNull(processInstanceExt, "流程实例拓展({}) 不存在", id); // 获得流程定义 ProcessDefinition processDefinition = processDefinitionService .getProcessDefinition(processInstance.getProcessDefinitionId()); Assert.notNull(processDefinition, "流程定义({}) 不存在", processInstance.getProcessDefinitionId()); BpmProcessDefinitionExtDO processDefinitionExt = processDefinitionService.getProcessDefinitionExt( processInstance.getProcessDefinitionId()); Assert.notNull(processDefinitionExt, "流程定义拓展({}) 不存在", id); String bpmnXml = processDefinitionService.getProcessDefinitionBpmnXML(processInstance.getProcessDefinitionId()); // 获得 User AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId())); DeptRespDTO dept = null; if (startUser != null) { dept = deptApi.getDept(startUser.getDeptId()); } // 拼接结果 return BpmProcessInstanceConvert.INSTANCE.convert2(processInstance, processInstanceExt, processDefinition, processDefinitionExt, bpmnXml, startUser, dept); } @Override public void cancelProcessInstance(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { // 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } // 只能取消自己的 if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF); } // 通过删除流程实例,实现流程实例的取消, // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。通过历史表查询 deleteProcessInstance(cancelReqVO.getId(), BpmProcessInstanceDeleteReasonEnum.CANCEL_TASK.format(cancelReqVO.getReason())); } /** * 获得历史的流程实例 * * @param id 流程实例的编号 * @return 历史的流程实例 */ @Override public HistoricProcessInstance getHistoricProcessInstance(String id) { return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).processInstanceTenantId(TenantContextHolder.getTenantId().toString()).singleResult(); } @Override public List getHistoricProcessInstances(Set ids) { return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).processInstanceTenantId(TenantContextHolder.getTenantId().toString()).list(); } @Override public void createProcessInstanceExt(ProcessInstance instance) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition2(instance.getProcessDefinitionId()); // 插入 BpmProcessInstanceExtDO 对象 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(instance.getId()) .setProcessDefinitionId(definition.getId()) .setName(instance.getProcessDefinitionName()) .setStartUserId(Long.valueOf(instance.getStartUserId())) .setCategory(definition.getCategory()) .setStatus(BpmProcessInstanceStatusEnum.RUNNING.getStatus()) .setResult(BpmProcessInstanceResultEnum.PROCESS.getResult()); processInstanceExtMapper.insert(instanceExtDO); } @Override public void updateProcessInstanceExtCancel(FlowableCancelledEvent event) { // 判断是否为 Reject 不通过。如果是,则不进行更新. // 因为,updateProcessInstanceExtReject 方法,已经进行更新了 if (BpmProcessInstanceDeleteReasonEnum.isRejectReason((String) event.getCause())) { return; } // 需要主动查询,因为 instance 只有 id 属性 // 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId()); // 更新拓展表 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(event.getProcessInstanceId()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置 .setEndTime(new Date()) .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.CANCEL.getResult()); processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } @Override public void updateProcessInstanceExtComplete(ProcessInstance instance) { // 需要主动查询,因为 instance 只有 id 属性 // 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId()); // 更新拓展表 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(instance.getProcessInstanceId()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置 .setEndTime(new Date()) .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) // 如果正常完全,说明审批通过 .setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()); processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程被通过的消息 messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.convert2ApprovedReq(instance)); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } @Override @Transactional(rollbackFor = Exception.class) public void updateProcessInstanceExtReject(String id, String reason) { // 需要主动查询,因为 instance 只有 id 属性 ProcessInstance processInstance = getProcessInstance(id); // 删除流程实例,以实现驳回任务时,取消整个审批流程 deleteProcessInstance(id, StrUtil.format(BpmProcessInstanceDeleteReasonEnum.REJECT_TASK.format(reason))); // 更新 status + result // 注意,不能和上面的逻辑更换位置。因为 deleteProcessInstance 会触发流程的取消,进而调用 updateProcessInstanceExtCancel 方法, // 设置 result 为 BpmProcessInstanceStatusEnum.CANCEL,显然和 result 不一定是一致的 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO().setProcessInstanceId(id) .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.REJECT.getResult()); processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程被不通过的消息 messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.convert2RejectReq(processInstance, reason)); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } private void deleteProcessInstance(String id, String reason) { runtimeService.deleteProcessInstance(id, reason); } private String createProcessInstance0(Long userId, ProcessDefinition definition, Map variables, String businessKey) { // 校验流程定义 if (definition == null) { throw exception(PROCESS_DEFINITION_NOT_EXISTS); } if (definition.isSuspended()) { throw exception(PROCESS_DEFINITION_IS_SUSPENDED); } // 创建流程实例 ProcessInstanceBuilder processInstanceBuilder = runtimeService.createProcessInstanceBuilder() .processDefinitionId(definition.getId()) .businessKey(businessKey) .variables(variables) .tenantId(TenantContextHolder.getTenantId().toString()); ProcessInstance instance = processInstanceBuilder.start(); // ProcessInstance instance = runtimeService.startProcessInstanceById(definition.getId(), businessKey, variables); // 设置流程名字 runtimeService.setProcessInstanceName(instance.getId(), definition.getName()); // 补全流程实例的拓展表 processInstanceExtMapper.updateByProcessInstanceId(new BpmProcessInstanceExtDO().setProcessInstanceId(instance.getId()) .setFormVariables(variables)); return instance.getId(); } } \ No newline at end of file diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/task/impl/BpmTaskServiceImpl.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/task/impl/BpmTaskServiceImpl.java new file mode 100644 index 00000000..cfb72a83 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/task/impl/BpmTaskServiceImpl.java @@ -0,0 +1,511 @@ +package com.ruoyi.flowable.service.task.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.common.utils.NumberUtils; +import com.ruoyi.common.utils.PageUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.flowable.convert.task.BpmTaskConvert; +import com.ruoyi.flowable.core.enums.definition.BpmTaskRuleScriptEnum; +import com.ruoyi.flowable.core.enums.task.BpmProcessInstanceDeleteReasonEnum; +import com.ruoyi.flowable.core.enums.task.BpmProcessInstanceResultEnum; +import com.ruoyi.flowable.domain.dto.user.AdminUserRespDTO; +import com.ruoyi.flowable.domain.dto.user.DeptRespDTO; +import com.ruoyi.flowable.domain.entity.task.BpmTaskExtDO; +import com.ruoyi.flowable.domain.vo.task.*; +import com.ruoyi.flowable.mapper.task.BpmTaskExtMapper; +import com.ruoyi.flowable.service.definition.BpmModelService; +import com.ruoyi.flowable.service.message.BpmMessageService; +import com.ruoyi.flowable.service.task.BpmProcessInstanceService; +import com.ruoyi.flowable.service.task.BpmTaskService; +import com.ruoyi.flowable.service.user.AdminUserApi; +import com.ruoyi.flowable.service.user.DeptApi; +import com.ruoyi.plugin.tenant.core.context.TenantContextHolder; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.MapUtils; +import org.flowable.bpmn.constants.BpmnXMLConstants; +import org.flowable.bpmn.model.FlowNode; +import org.flowable.engine.HistoryService; +import org.flowable.engine.IdentityService; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.TaskService; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.runtime.ActivityInstance; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.idm.api.User; +import org.flowable.task.api.Task; +import org.flowable.task.api.TaskQuery; +import org.flowable.task.api.history.HistoricTaskInstance; +import org.flowable.task.api.history.HistoricTaskInstanceQuery; +import org.flowable.task.service.impl.persistence.entity.TaskEntity; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionSynchronization; +import org.springframework.transaction.support.TransactionSynchronizationManager; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.*; +import java.util.stream.Collectors; + +import static com.ruoyi.common.exception.util.ServiceExceptionUtil.exception; +import static com.ruoyi.common.utils.collection.CollectionUtils.convertMap; +import static com.ruoyi.common.utils.collection.CollectionUtils.convertSet; +import static com.ruoyi.flowable.core.enums.ErrorCodeConstants.*; + + +/** + * 流程任务实例 Service 实现类 + *

+ * hasPermi + * + * @author jason + */ +@Slf4j +@Service +public class BpmTaskServiceImpl implements BpmTaskService { + + @Resource + private RuntimeService runtimeService; + @Resource + private TaskService taskService; + @Resource + private HistoryService historyService; + + @Resource + private BpmProcessInstanceService processInstanceService; + + @Resource + private BpmModelService modelService; + + @Resource + protected IdentityService identityService; + + @Resource + private AdminUserApi adminUserApi; + + @Resource + private DeptApi deptApi; + + @Resource + private BpmTaskExtMapper taskExtMapper; + @Resource + private BpmMessageService messageService; + + @Override + public PageResult getTodoTaskPage(Long userId, BpmTaskTodoPageReqVO pageVO) { + // 查询待办任务 + TaskQuery taskQuery = taskService.createTaskQuery().taskAssignee(String.valueOf(userId)).taskTenantId(TenantContextHolder.getTenantId().toString()) // 分配给自己 + .orderByTaskCreateTime().desc(); // 创建时间倒序 + if (StrUtil.isNotBlank(pageVO.getName())) { + taskQuery.taskNameLike("%" + pageVO.getName() + "%"); + } + if (pageVO.getBeginCreateTime() != null) { + taskQuery.taskCreatedAfter(pageVO.getBeginCreateTime()); + } + if (pageVO.getEndCreateTime() != null) { + taskQuery.taskCreatedBefore(pageVO.getEndCreateTime()); + } + // 执行查询 + List tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize()); + if (CollUtil.isEmpty(tasks)) { + return PageResult.empty(taskQuery.count()); + } + + // 获得 ProcessInstance Map + Map processInstanceMap = + processInstanceService.getProcessInstanceMap(convertSet(tasks, Task::getProcessInstanceId)); + // 获得 User Map + Map userMap = adminUserApi.getUserMap(convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId()))); + // 拼接结果 + return new PageResult<>(BpmTaskConvert.INSTANCE.convertList1(tasks, processInstanceMap, userMap), taskQuery.count()); + } + + @Override + public PageResult getDoneTaskPage(Long userId, BpmTaskDonePageReqVO pageVO) { + // 查询已办任务 + // 已完成 + HistoricTaskInstanceQuery taskQuery = historyService.createHistoricTaskInstanceQuery().finished() + // 分配给自己 + .taskAssignee(String.valueOf(userId)) + // 审批时间倒序 + .orderByHistoricTaskInstanceEndTime().desc(); + if (StrUtil.isNotBlank(pageVO.getName())) { + taskQuery.taskNameLike("%" + pageVO.getName() + "%"); + } + if (pageVO.getBeginCreateTime() != null) { + taskQuery.taskCreatedAfter(pageVO.getBeginCreateTime()); + } + if (pageVO.getEndCreateTime() != null) { + taskQuery.taskCreatedBefore(pageVO.getEndCreateTime()); + } + // 执行查询 + List tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize()); + if (CollUtil.isEmpty(tasks)) { + return PageResult.empty(taskQuery.count()); + } + + // 获得 TaskExtDO Map + List bpmTaskExtDOs = + taskExtMapper.selectListByTaskIds(convertSet(tasks, HistoricTaskInstance::getId)); + Map bpmTaskExtDOMap = convertMap(bpmTaskExtDOs, BpmTaskExtDO::getTaskId); + // 获得 ProcessInstance Map + Map historicProcessInstanceMap = + processInstanceService.getHistoricProcessInstanceMap( + convertSet(tasks, HistoricTaskInstance::getProcessInstanceId)); + // 获得 User Map + Map userMap = adminUserApi.getUserMap(convertSet(historicProcessInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId()))); + // 拼接结果 + return new PageResult<>(BpmTaskConvert.INSTANCE.convertList2(tasks, bpmTaskExtDOMap, historicProcessInstanceMap, userMap), taskQuery.count()); + } + + @Override + public List getTasksByProcessInstanceIds(List processInstanceIds) { + if (CollUtil.isEmpty(processInstanceIds)) { + return Collections.emptyList(); + } + return taskService.createTaskQuery().processInstanceIdIn(processInstanceIds).taskTenantId(TenantContextHolder.getTenantId().toString()).list(); + } + + @Override + public List getTaskListByProcessInstanceId(String processInstanceId) { + // 获得任务列表 + List tasks = historyService.createHistoricTaskInstanceQuery() + .processInstanceId(processInstanceId) + .orderByHistoricTaskInstanceStartTime().desc() // 创建时间倒序 + .list(); + if (CollUtil.isEmpty(tasks)) { + return Collections.emptyList(); + } + + // 获得 TaskExtDO Map + List bpmTaskExtDOs = taskExtMapper.selectListByTaskIds(convertSet(tasks, HistoricTaskInstance::getId)); + Map bpmTaskExtDOMap = convertMap(bpmTaskExtDOs, BpmTaskExtDO::getTaskId); + // 获得 ProcessInstance Map + HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(processInstanceId); + // 获得 User Map + Set userIds = convertSet(tasks, task -> NumberUtils.parseLong(task.getAssignee())); + userIds.add(NumberUtils.parseLong(processInstance.getStartUserId())); + Map userMap = adminUserApi.getUserMap(userIds); + // 获得 Dept Map + Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); + + // 拼接数据 + return BpmTaskConvert.INSTANCE.convertList3(tasks, bpmTaskExtDOMap, processInstance, userMap, deptMap); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void approveTask(Long userId, @Valid BpmTaskApproveReqVO reqVO) { + // 校验任务存在 + Task task = checkTask(userId, reqVO.getId()); + // 校验流程实例存在 + ProcessInstance instance = processInstanceService.getProcessInstance(task.getProcessInstanceId()); + if (instance == null) { + throw exception(PROCESS_INSTANCE_NOT_EXISTS); + } + + // 完成任务,审批通过 + taskService.complete(task.getId(), instance.getProcessVariables()); + + // 更新任务拓展表为通过 + taskExtMapper.updateByTaskId( + new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()) + .setReason(reqVO.getReason())); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void rejectTask(Long userId, @Valid BpmTaskRejectReqVO reqVO) { + Task task = checkTask(userId, reqVO.getId()); + // 校验流程实例存在 + ProcessInstance instance = processInstanceService.getProcessInstance(task.getProcessInstanceId()); + if (instance == null) { + throw exception(PROCESS_INSTANCE_NOT_EXISTS); + } + + // 更新流程实例为不通过 + processInstanceService.updateProcessInstanceExtReject(instance.getProcessInstanceId(), reqVO.getReason()); + + // 更新任务拓展表为不通过 + taskExtMapper.updateByTaskId( + new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.REJECT.getResult()) + .setEndTime(new Date()).setReason(reqVO.getReason())); + } + + @Override + public void updateTaskAssignee(Long userId, BpmTaskUpdateAssigneeReqVO reqVO) { + // 校验任务存在 + Task task = checkTask(userId, reqVO.getId()); + // 更新负责人 + updateTaskAssignee(task.getId(), reqVO.getAssigneeUserId()); + } + + @Override + public void updateTaskAssignee(String id, Long userId) { + taskService.setAssignee(id, String.valueOf(userId)); + } + + /** + * 校验任务是否存在, 并且是否是分配给自己的任务 + * + * @param userId 用户 id + * @param taskId task id + */ + private Task checkTask(Long userId, String taskId) { + Task task = getTask(taskId); + if (task == null) { + throw exception(TASK_COMPLETE_FAIL_NOT_EXISTS); + } + if (!Objects.equals(userId, NumberUtils.parseLong(task.getAssignee()))) { + throw exception(TASK_COMPLETE_FAIL_ASSIGN_NOT_SELF); + } + return task; + } + + @Override + public void createTaskExt(Task task) { + BpmTaskExtDO taskExtDO = + BpmTaskConvert.INSTANCE.convert2TaskExt(task).setResult(BpmProcessInstanceResultEnum.PROCESS.getResult()); + taskExtMapper.insert(taskExtDO); + } + + @Override + public void updateTaskExtComplete(Task task) { + BpmTaskExtDO taskExtDO = BpmTaskConvert.INSTANCE.convert2TaskExt(task) + // 不设置也问题不大,因为 Complete 一般是审核通过,已经设置 + .setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()) + .setEndTime(new Date()); + taskExtMapper.updateByTaskId(taskExtDO); + } + + @Override + public void updateTaskExtCancel(String taskId) { + // 需要在事务提交后,才进行查询。不然查询不到历史的原因 + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { + + @Override + public void afterCommit() { + // 可能只是活动,不是任务,所以查询不到 + HistoricTaskInstance task = getHistoricTask(taskId); + if (task == null) { + return; + } + // 如果任务拓展表已经是完成的状态,则跳过 + BpmTaskExtDO taskExt = taskExtMapper.selectByTaskId(taskId); + if (taskExt == null) { + log.error("[updateTaskExtCancel][taskId({}) 查找不到对应的记录,可能存在问题]", taskId); + return; + } + // 如果已经是最终的结果,则跳过 + if (BpmProcessInstanceResultEnum.isEndResult(taskExt.getResult())) { + log.error("[updateTaskExtCancel][taskId({}) 处于结果({}),无需进行更新]", taskId, taskExt.getResult()); + return; + } + + // 更新任务 + taskExtMapper.updateById(new BpmTaskExtDO().setId(taskExt.getId()).setResult(BpmProcessInstanceResultEnum.CANCEL.getResult()) + .setEndTime(new Date()).setReason(BpmProcessInstanceDeleteReasonEnum.translateReason(task.getDeleteReason()))); + } + + }); + } + + @Override + public void updateTaskExtAssign(Task task) { + BpmTaskExtDO taskExtDO = + new BpmTaskExtDO().setAssigneeUserId(NumberUtils.parseLong(task.getAssignee())).setTaskId(task.getId()); + taskExtMapper.updateByTaskId(taskExtDO); + // 发送通知。在事务提交时,批量执行操作,所以直接查询会无法查询到 ProcessInstance,所以这里是通过监听事务的提交来实现。 + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { + @Override + public void afterCommit() { + ProcessInstance processInstance = + processInstanceService.getProcessInstance(task.getProcessInstanceId()); + // TODO: 2022/8/1 + AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId())); + messageService.sendMessageWhenTaskAssigned(BpmTaskConvert.INSTANCE.convert(processInstance, startUser, task)); + } + }); + } + + @Override + public List getBackNodesByProcessInstanceId(String processInstanceId, String taskId) { + List backNods = new ArrayList<>(); + TaskEntity taskEntity = (TaskEntity) taskService.createTaskQuery().taskId(taskId).taskTenantId(TenantContextHolder.getTenantId().toString()).singleResult(); + String currActId = taskEntity.getTaskDefinitionKey(); + //获取运行节点表中usertask + String sql = "select t.* from act_ru_actinst t where t.ACT_TYPE_ = 'userTask' " + + " and t.PROC_INST_ID_=#{processInstanceId} and t.END_TIME_ is not null "; + List activityInstances = runtimeService.createNativeActivityInstanceQuery().sql(sql) + .parameter("processInstanceId", processInstanceId) + .list(); + //获取运行节点表的parallelGateway节点并出重 + sql = "SELECT t.ID_, t.REV_,t.PROC_DEF_ID_,t.PROC_INST_ID_,t.EXECUTION_ID_,t.ACT_ID_, t.TASK_ID_, t.CALL_PROC_INST_ID_, t.ACT_NAME_, t.ACT_TYPE_, " + + " t.ASSIGNEE_, t.START_TIME_, max(t.END_TIME_) as END_TIME_, t.DURATION_, t.DELETE_REASON_, t.TENANT_ID_" + + " FROM act_ru_actinst t WHERE t.ACT_TYPE_ = 'parallelGateway' AND t.PROC_INST_ID_ = #{processInstanceId} and t.END_TIME_ is not null" + + " and t.ACT_ID_ <> #{actId} GROUP BY t.act_id_"; + List parallelGatewaies = runtimeService.createNativeActivityInstanceQuery().sql(sql) + .parameter("processInstanceId", processInstanceId) + .parameter("actId", currActId) + .list(); + //排序 + if (CollectionUtils.isNotEmpty(parallelGatewaies)) { + activityInstances.addAll(parallelGatewaies); + activityInstances.sort(Comparator.comparing(ActivityInstance::getEndTime)); + } + //分组节点 + int count = 0; + Map> parallelGatewayUserTasks = new HashMap<>(); + List userTasks = new ArrayList<>(); + ActivityInstance currActivityInstance = null; + for (ActivityInstance activityInstance : activityInstances) { + if (BpmnXMLConstants.ELEMENT_GATEWAY_PARALLEL.equals(activityInstance.getActivityType())) { + count++; + if (count % 2 != 0) { + List datas = new ArrayList<>(); + currActivityInstance = activityInstance; + parallelGatewayUserTasks.put(currActivityInstance, datas); + } + } + if (BpmnXMLConstants.ELEMENT_TASK_USER.equals(activityInstance.getActivityType())) { + if (count % 2 == 0) { + userTasks.add(activityInstance); + } else { + if (parallelGatewayUserTasks.containsKey(currActivityInstance)) { + parallelGatewayUserTasks.get(currActivityInstance).add(activityInstance); + } + } + } + } + //组装人员名称 + List historicTaskInstances = historyService.createHistoricTaskInstanceQuery() + .processInstanceId(processInstanceId).finished().list(); + Map> taskInstanceMap = new HashMap<>(); + List userCodes = new ArrayList<>(); + historicTaskInstances.forEach(historicTaskInstance -> { + userCodes.add(historicTaskInstance.getAssignee()); + String taskDefinitionKey = historicTaskInstance.getTaskDefinitionKey(); + if (taskInstanceMap.containsKey(historicTaskInstance.getTaskDefinitionKey())) { + taskInstanceMap.get(taskDefinitionKey).add(historicTaskInstance); + } else { + List tasks = new ArrayList<>(); + tasks.add(historicTaskInstance); + taskInstanceMap.put(taskDefinitionKey, tasks); + } + }); + //组装usertask的数据 + List userList = identityService.createUserQuery().userIds(userCodes).list(); + Map activityIdUserNames = this.getApplyers(processInstanceId, userList, taskInstanceMap); + if (CollectionUtils.isNotEmpty(userTasks)) { + userTasks.forEach(activityInstance -> { + FlowNodeVo node = new FlowNodeVo(); + node.setNodeId(activityInstance.getActivityId()); + node.setNodeName(activityInstance.getActivityName()); + node.setEndTime(activityInstance.getEndTime()); + node.setUserName(activityIdUserNames.get(activityInstance.getActivityId())); + backNods.add(node); + }); + } + //组装会签节点数据 + if (MapUtils.isNotEmpty(taskInstanceMap)) { + parallelGatewayUserTasks.forEach((activity, activities) -> { + FlowNodeVo node = new FlowNodeVo(); + node.setNodeId(activity.getActivityId()); + node.setEndTime(activity.getEndTime()); + StringBuffer nodeNames = new StringBuffer("会签:"); + StringBuffer userNames = new StringBuffer("审批人员:"); + if (CollectionUtils.isNotEmpty(activities)) { + activities.forEach(activityInstance -> { + nodeNames.append(activityInstance.getActivityName()).append(","); + userNames.append(activityIdUserNames.get(activityInstance.getActivityId())).append(","); + }); + node.setNodeName(nodeNames.toString()); + node.setUserName(userNames.toString()); + backNods.add(node); + } + }); + } + //去重合并 + List datas = backNods.stream().collect( + Collectors.collectingAndThen(Collectors.toCollection(() -> + new TreeSet<>(Comparator.comparing(nodeVo -> nodeVo.getNodeId()))), ArrayList::new)); + + //排序 + datas.sort(Comparator.comparing(FlowNodeVo::getEndTime)); + return datas; + } + + @Override + public Boolean backToStepTask(BackTaskVo backTaskVo) { + TaskEntity taskEntity = (TaskEntity) taskService.createTaskQuery().taskId(backTaskVo.getTaskId()).taskTenantId(TenantContextHolder.getTenantId().toString()).singleResult(); + //TODO 添加驳回功能 + //1.把当前的节点设置为空 + if (taskEntity != null) { + //2.设置审批人 + taskEntity.setAssignee(backTaskVo.getUserCode()); + taskService.saveTask(taskEntity); + //3.添加驳回意见 + //4.处理提交人节点 + FlowNode distActivity = modelService.findFlowNodeByActivityId(taskEntity.getProcessDefinitionId(), backTaskVo.getDistFlowElementId()); + if (distActivity != null) { + if ("提交人节点名称".equals(distActivity.getName())) { + ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(taskEntity.getProcessInstanceId()).processInstanceTenantId(TenantContextHolder.getTenantId().toString()).singleResult(); + runtimeService.setVariable(backTaskVo.getProcessInstanceId(), "提交人的变量名称", processInstance.getStartUserId()); + } + } + //5.删除节点 + //6.判断节点是不是子流程内部的节点 + if (true) { + //6.1 子流程内部驳回 + } else { + //6.2 普通驳回 + } + return Boolean.TRUE; + } else { + return Boolean.FALSE; + } + } + + private Task getTask(String id) { + return taskService.createTaskQuery().taskId(id).taskTenantId(TenantContextHolder.getTenantId().toString()).singleResult(); + } + + private HistoricTaskInstance getHistoricTask(String id) { + return historyService.createHistoricTaskInstanceQuery().taskId(id).singleResult(); + } + + private Map getApplyers(String processInstanceId, List userList, Map> taskInstanceMap) { + Map userMap = userList.stream().collect(Collectors.toMap(User::getId, user -> user)); + Map applyMap = new HashMap<>(); + ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).processInstanceTenantId(TenantContextHolder.getTenantId().toString()).singleResult(); + taskInstanceMap.forEach((activityId, taskInstances) -> { + StringBuffer applyers = new StringBuffer(); + StringBuffer finalApplyers = applyers; + taskInstances.forEach(taskInstance -> { + if (!taskInstance.getName().equals(BpmTaskRuleScriptEnum.START_USER.getDesc())) { + User user = userMap.get(taskInstance.getAssignee()); + if (user != null) { + if (StringUtils.indexOf(finalApplyers.toString(), user.getDisplayName()) == -1) { + finalApplyers.append(user.getDisplayName()).append(","); + } + } + } else { + String startUserId = processInstance.getStartUserId(); + User user = identityService.createUserQuery().userId(startUserId).singleResult(); + if (user != null) { + finalApplyers.append(user.getDisplayName()).append(","); + } + } + }); + if (applyers.length() > 0) { + applyers = applyers.deleteCharAt(applyers.length() - 1); + } + applyMap.put(activityId, applyers.toString()); + }); + return applyMap; + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/AdminUserApi.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/AdminUserApi.java new file mode 100644 index 00000000..f7756240 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/AdminUserApi.java @@ -0,0 +1,71 @@ +package com.ruoyi.flowable.service.user; + + +import com.ruoyi.common.utils.collection.CollectionUtils; +import com.ruoyi.flowable.domain.dto.user.AdminUserRespDTO; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Admin 用户 API 接口 + * + * hasPermi + */ +public interface AdminUserApi { + + /** + * 通过用户 ID 查询用户 + * + * @param id 用户ID + * @return 用户对象信息 + */ + AdminUserRespDTO getUser(Long id); + + /** + * 通过用户 ID 查询用户们 + * + * @param ids 用户 ID 们 + * @return 用户对象信息 + */ + List getUsers(Collection ids); + + /** + * 获得指定部门的用户数组 + * + * @param deptIds 部门数组 + * @return 用户数组 + */ + List getUsersByDeptIds(Collection deptIds); + + /** + * 获得指定岗位的用户数组 + * + * @param postIds 岗位数组 + * @return 用户数组 + */ + List getUsersByPostIds(Collection postIds); + + /** + * 获得用户 Map + * + * @param ids 用户编号数组 + * @return 用户 Map + */ + default Map getUserMap(Collection ids) { + List users = getUsers(ids); + return CollectionUtils.convertMap(users, AdminUserRespDTO::getId); + } + + /** + * 校验用户们是否有效。如下情况,视为无效: + * 1. 用户编号不存在 + * 2. 用户被禁用 + * + * @param ids 用户编号数组 + */ + void validUsers(Set ids); + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/DeptApi.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/DeptApi.java new file mode 100644 index 00000000..e512c4f5 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/DeptApi.java @@ -0,0 +1,54 @@ +package com.ruoyi.flowable.service.user; + +import com.ruoyi.common.utils.collection.CollectionUtils; +import com.ruoyi.flowable.domain.dto.user.DeptRespDTO; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 部门 API 接口 + * + * hasPermi + */ +public interface DeptApi { + + /** + * 获得部门信息 + * + * @param id 部门编号 + * @return 部门信息 + */ + DeptRespDTO getDept(Long id); + + /** + * 获得部门信息数组 + * + * @param ids 部门编号数组 + * @return 部门信息数组 + */ + List getDepts(Collection ids); + + /** + * 校验部门们是否有效。如下情况,视为无效: + * 1. 部门编号不存在 + * 2. 部门被禁用 + * + * @param ids 角色编号数组 + */ + void validDepts(Collection ids); + + /** + * 获得指定编号的部门 Map + * + * @param ids 部门编号数组 + * @return 部门 Map + */ + default Map getDeptMap(Set ids) { + List list = getDepts(ids); + return CollectionUtils.convertMap(list, DeptRespDTO::getId); + } + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/DictDataApi.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/DictDataApi.java new file mode 100644 index 00000000..f4ffcf6e --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/DictDataApi.java @@ -0,0 +1,23 @@ +package com.ruoyi.flowable.service.user; + +import java.util.Collection; + +/** + * 字典数据 API 接口 + * + * hasPermi + */ +public interface DictDataApi { + + /** + * 校验字典数据们是否有效。如下情况,视为无效: + * 1. 字典数据不存在 + * 2. 字典数据被禁用 + * + * @param dictType 字典类型 + * @param values 字典数据值的数组 + */ + void validDictDatas(String dictType, Collection values); + + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/PermissionApi.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/PermissionApi.java new file mode 100644 index 00000000..a964dc18 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/PermissionApi.java @@ -0,0 +1,21 @@ +package com.ruoyi.flowable.service.user; + +import java.util.Collection; +import java.util.Set; + +/** + * 权限 API 接口 + * + * hasPermi + */ +public interface PermissionApi { + + /** + * 获得拥有多个角色的用户编号集合 + * + * @param roleIds 角色编号集合 + * @return 用户编号集合 + */ + Set getUserRoleIdListByRoleIds(Collection roleIds); + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/PostApi.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/PostApi.java new file mode 100644 index 00000000..454079d0 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/PostApi.java @@ -0,0 +1,21 @@ +package com.ruoyi.flowable.service.user; + +import java.util.Collection; + +/** + * 岗位 API 接口 + * + * hasPermi + */ +public interface PostApi { + + /** + * 校验岗位们是否有效。如下情况,视为无效: + * 1. 岗位编号不存在 + * 2. 岗位被禁用 + * + * @param ids 岗位编号数组 + */ + void validPosts(Collection ids); + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/RoleApi.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/RoleApi.java new file mode 100644 index 00000000..b053ab93 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/RoleApi.java @@ -0,0 +1,21 @@ +package com.ruoyi.flowable.service.user; + +import java.util.Collection; + +/** + * 角色 API 接口 + * + * hasPermi + */ +public interface RoleApi { + + /** + * 校验角色们是否有效。如下情况,视为无效: + * 1. 角色编号不存在 + * 2. 角色被禁用 + * + * @param ids 角色编号数组 + */ + void validRoles(Collection ids); + +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/impl/AdminUserApiImpl.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/impl/AdminUserApiImpl.java new file mode 100644 index 00000000..101efd70 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/impl/AdminUserApiImpl.java @@ -0,0 +1,82 @@ +package com.ruoyi.flowable.service.user.impl; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.enums.CommonStatusEnum; +import com.ruoyi.common.utils.collection.CollectionUtils; +import com.ruoyi.flowable.convert.user.UserConvert; +import com.ruoyi.flowable.domain.dto.user.AdminUserRespDTO; +import com.ruoyi.flowable.service.user.AdminUserApi; +import com.ruoyi.system.service.ISysUserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.*; + +import static com.ruoyi.common.exception.util.ServiceExceptionUtil.exception; +import static com.ruoyi.flowable.core.enums.user.ErrorCodeConstants.USER_NOT_EXISTS; +import static com.ruoyi.flowable.core.enums.user.SysErrorCodeConstants.USER_IS_DISABLE; + +/** + * Admin 用户 API 实现类 + *

+ * hasPermi + */ +@Service +public class AdminUserApiImpl implements AdminUserApi { + + @Autowired + private ISysUserService userService; + + @Override + public AdminUserRespDTO getUser(Long id) { + SysUser sysUser = userService.selectUserById(id); + return UserConvert.INSTANCE.convert(sysUser); + } + + @Override + public List getUsers(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + List sysUsers = userService.listByIds(ids); + return UserConvert.INSTANCE.convertList(sysUsers); + } + + @Override + public List getUsersByDeptIds(Collection deptIds) { + if (CollUtil.isEmpty(deptIds)) { + return Collections.emptyList(); + } + List sysUsers = userService.list(Wrappers.query().lambda().in(SysUser::getDeptId, deptIds)); + return UserConvert.INSTANCE.convertList(sysUsers); + } + + @Override + public List getUsersByPostIds(Collection postIds) { + List sysUsers = userService.getUsersByPostIds(postIds); + return UserConvert.INSTANCE.convertList(sysUsers); + } + + @Override + public void validUsers(Set ids) { + if (CollUtil.isEmpty(ids)) { + return; + } + // 获得岗位信息 + List users = userService.listByIds(ids); + Map userMap = CollectionUtils.convertMap(users, SysUser::getUserId); + // 校验 + ids.forEach(id -> { + SysUser user = userMap.get(id); + if (user == null) { + throw exception(USER_NOT_EXISTS); + } + if (!CommonStatusEnum.ENABLE.getStatus().equals(Integer.parseInt(user.getStatus()))) { + throw exception(USER_IS_DISABLE, user.getNickName()); + } + }); + + } +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/impl/DeptApiImpl.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/impl/DeptApiImpl.java new file mode 100644 index 00000000..187050a8 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/impl/DeptApiImpl.java @@ -0,0 +1,70 @@ +package com.ruoyi.flowable.service.user.impl; + +import cn.hutool.core.collection.CollUtil; +import com.ruoyi.common.core.domain.entity.SysDept; +import com.ruoyi.common.enums.CommonStatusEnum; +import com.ruoyi.common.utils.collection.CollectionUtils; +import com.ruoyi.flowable.convert.user.DeptConvert; +import com.ruoyi.flowable.domain.dto.user.DeptRespDTO; +import com.ruoyi.flowable.service.user.DeptApi; +import com.ruoyi.system.service.ISysDeptService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static com.ruoyi.common.exception.util.ServiceExceptionUtil.exception; +import static com.ruoyi.flowable.core.enums.user.SysErrorCodeConstants.DEPT_NOT_ENABLE; +import static com.ruoyi.flowable.core.enums.user.SysErrorCodeConstants.DEPT_NOT_FOUND; + +/** + * 部门 API 实现类 + *

+ * hasPermi + * + * @author wangzongrun + */ +@Service +public class DeptApiImpl implements DeptApi { + + @Autowired + private ISysDeptService deptService; + + @Override + public DeptRespDTO getDept(Long id) { + SysDept sysDept = deptService.selectDeptById(id); + return DeptConvert.INSTANCE.convert(sysDept); + } + + @Override + public List getDepts(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + List sysDepts = deptService.listByIds(ids); + return DeptConvert.INSTANCE.convertList(sysDepts); + } + + @Override + public void validDepts(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return; + } + // 获得科室信息 + List depts = deptService.listByIds(ids); + Map deptMap = CollectionUtils.convertMap(depts, SysDept::getDeptId); + // 校验 + ids.forEach(id -> { + SysDept dept = deptMap.get(id); + if (dept == null) { + throw exception(DEPT_NOT_FOUND); + } + if (!CommonStatusEnum.ENABLE.getStatus().equals(Integer.parseInt(dept.getStatus()))) { + throw exception(DEPT_NOT_ENABLE, dept.getDeptName()); + } + }); + } +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/impl/DictDataApiImpl.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/impl/DictDataApiImpl.java new file mode 100644 index 00000000..b87bdced --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/impl/DictDataApiImpl.java @@ -0,0 +1,53 @@ +package com.ruoyi.flowable.service.user.impl; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.ruoyi.common.core.domain.entity.SysDictData; +import com.ruoyi.common.enums.CommonStatusEnum; +import com.ruoyi.common.utils.collection.CollectionUtils; +import com.ruoyi.flowable.service.user.DictDataApi; +import com.ruoyi.system.service.ISysDictDataService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static com.ruoyi.common.exception.util.ServiceExceptionUtil.exception; +import static com.ruoyi.flowable.core.enums.user.SysErrorCodeConstants.DICT_DATA_NOT_ENABLE; +import static com.ruoyi.flowable.core.enums.user.SysErrorCodeConstants.DICT_DATA_NOT_EXISTS; + + +/** + * 字典数据 API 实现类 + *

+ * hasPermi + */ +@Service +public class DictDataApiImpl implements DictDataApi { + + @Autowired + private ISysDictDataService dictDataService; + + @Override + public void validDictDatas(String dictType, Collection values) { + if (CollUtil.isEmpty(values)) { + return; + } + List list = dictDataService.list( + Wrappers.query().lambda().eq(SysDictData::getDictType, dictType).in(SysDictData::getDictValue, values)); + Map dictDataMap = CollectionUtils.convertMap( + list, SysDictData::getDictValue); + // 校验 + values.forEach(value -> { + SysDictData dictData = dictDataMap.get(value); + if (dictData == null) { + throw exception(DICT_DATA_NOT_EXISTS); + } + if (!CommonStatusEnum.ENABLE.getStatus().equals(Integer.parseInt(dictData.getStatus()))) { + throw exception(DICT_DATA_NOT_ENABLE, dictData.getDictLabel()); + } + }); + } +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/impl/PermissionApiImpl.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/impl/PermissionApiImpl.java new file mode 100644 index 00000000..36a5f9a7 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/impl/PermissionApiImpl.java @@ -0,0 +1,26 @@ +package com.ruoyi.flowable.service.user.impl; + +import com.ruoyi.flowable.service.user.PermissionApi; +import com.ruoyi.system.service.ISysUserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.Set; + +/** + * 权限 API 实现类 + * + * hasPermi + */ +@Service +public class PermissionApiImpl implements PermissionApi { + + @Autowired + private ISysUserService userService; + + @Override + public Set getUserRoleIdListByRoleIds(Collection roleIds) { + return userService.getUserRoleIdListByRoleIds(roleIds); + } +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/impl/PostApiImpl.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/impl/PostApiImpl.java new file mode 100644 index 00000000..39b6982f --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/impl/PostApiImpl.java @@ -0,0 +1,50 @@ +package com.ruoyi.flowable.service.user.impl; + +import cn.hutool.core.collection.CollUtil; +import com.ruoyi.common.enums.CommonStatusEnum; +import com.ruoyi.common.utils.collection.CollectionUtils; +import com.ruoyi.flowable.service.user.PostApi; +import com.ruoyi.system.domain.SysPost; +import com.ruoyi.system.service.ISysPostService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static com.ruoyi.common.exception.util.ServiceExceptionUtil.exception; +import static com.ruoyi.flowable.core.enums.user.SysErrorCodeConstants.POST_NOT_ENABLE; +import static com.ruoyi.flowable.core.enums.user.SysErrorCodeConstants.POST_NOT_FOUND; + +/** + * 岗位 API 实现类 + *

+ * hasPermi + */ +@Service +public class PostApiImpl implements PostApi { + + @Autowired + private ISysPostService postService; + + @Override + public void validPosts(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return; + } + // 获得岗位信息 + List posts = postService.listByIds(ids); + Map postMap = CollectionUtils.convertMap(posts, SysPost::getPostId); + // 校验 + ids.forEach(id -> { + SysPost post = postMap.get(id); + if (post == null) { + throw exception(POST_NOT_FOUND); + } + if (!CommonStatusEnum.ENABLE.getStatus().equals(Integer.parseInt(post.getStatus()))) { + throw exception(POST_NOT_ENABLE, post.getPostName()); + } + }); + } +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/impl/RoleApiImpl.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/impl/RoleApiImpl.java new file mode 100644 index 00000000..80c3bd75 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/user/impl/RoleApiImpl.java @@ -0,0 +1,51 @@ +package com.ruoyi.flowable.service.user.impl; + +import cn.hutool.core.collection.CollUtil; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.enums.CommonStatusEnum; +import com.ruoyi.common.utils.collection.CollectionUtils; +import com.ruoyi.flowable.service.user.RoleApi; +import com.ruoyi.system.service.ISysRoleService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static com.ruoyi.common.exception.util.ServiceExceptionUtil.exception; +import static com.ruoyi.flowable.core.enums.user.SysErrorCodeConstants.ROLE_IS_DISABLE; +import static com.ruoyi.flowable.core.enums.user.SysErrorCodeConstants.ROLE_NOT_EXISTS; + + +/** + * 角色 API 实现类 + *

+ * hasPermi + */ +@Service +public class RoleApiImpl implements RoleApi { + + @Autowired + private ISysRoleService roleService; + + @Override + public void validRoles(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return; + } + // 获得角色信息 + List roles = roleService.listByIds(ids); + Map roleMap = CollectionUtils.convertMap(roles, SysRole::getRoleId); + // 校验 + ids.forEach(id -> { + SysRole role = roleMap.get(id); + if (role == null) { + throw exception(ROLE_NOT_EXISTS); + } + if (!CommonStatusEnum.ENABLE.getStatus().equals(Integer.parseInt(role.getStatus()))) { + throw exception(ROLE_IS_DISABLE, role.getRoleName()); + } + }); + } +} diff --git a/ruoyi-flowable/src/main/resources/mapper/flowable/BpmOALeaveMapper.xml b/ruoyi-flowable/src/main/resources/mapper/flowable/BpmOALeaveMapper.xml new file mode 100644 index 00000000..8d88b183 --- /dev/null +++ b/ruoyi-flowable/src/main/resources/mapper/flowable/BpmOALeaveMapper.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + select id, user_id, type, reason, start_time, end_time, day, result, process_instance_id, creator, create_time, updater, update_time, deleted, tenant_id from bpm_oa_leave + + + + + diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/config/FlowableConfiguration.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/config/FlowableConfiguration.class new file mode 100644 index 00000000..67d9cdbd Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/config/FlowableConfiguration.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/controller/definition/BpmFormController.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/controller/definition/BpmFormController.class new file mode 100644 index 00000000..580e4325 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/controller/definition/BpmFormController.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/controller/definition/BpmModelController.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/controller/definition/BpmModelController.class new file mode 100644 index 00000000..4631053f Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/controller/definition/BpmModelController.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/controller/definition/BpmProcessDefinitionController.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/controller/definition/BpmProcessDefinitionController.class new file mode 100644 index 00000000..b0ba9d57 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/controller/definition/BpmProcessDefinitionController.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/controller/definition/BpmTaskAssignRuleController.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/controller/definition/BpmTaskAssignRuleController.class new file mode 100644 index 00000000..270e3bcb Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/controller/definition/BpmTaskAssignRuleController.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/controller/definition/BpmUserGroupController.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/controller/definition/BpmUserGroupController.class new file mode 100644 index 00000000..529d3aca Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/controller/definition/BpmUserGroupController.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/controller/oa/BpmOALeaveController.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/controller/oa/BpmOALeaveController.class new file mode 100644 index 00000000..e495fe41 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/controller/oa/BpmOALeaveController.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/controller/task/BpmActivityController.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/controller/task/BpmActivityController.class new file mode 100644 index 00000000..31bbf8e8 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/controller/task/BpmActivityController.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/controller/task/BpmProcessInstanceController.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/controller/task/BpmProcessInstanceController.class new file mode 100644 index 00000000..51212a19 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/controller/task/BpmProcessInstanceController.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/controller/task/BpmTaskController.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/controller/task/BpmTaskController.class new file mode 100644 index 00000000..c18da0b6 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/controller/task/BpmTaskController.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/definition/BpmFormConvert.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/definition/BpmFormConvert.class new file mode 100644 index 00000000..68eacf51 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/definition/BpmFormConvert.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/definition/BpmFormConvertImpl.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/definition/BpmFormConvertImpl.class new file mode 100644 index 00000000..0779fd3b Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/definition/BpmFormConvertImpl.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/definition/BpmModelConvert.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/definition/BpmModelConvert.class new file mode 100644 index 00000000..21d208a4 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/definition/BpmModelConvert.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/definition/BpmModelConvertImpl.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/definition/BpmModelConvertImpl.class new file mode 100644 index 00000000..3b98fa3a Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/definition/BpmModelConvertImpl.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/definition/BpmProcessDefinitionConvert.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/definition/BpmProcessDefinitionConvert.class new file mode 100644 index 00000000..17b77f62 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/definition/BpmProcessDefinitionConvert.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/definition/BpmProcessDefinitionConvertImpl.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/definition/BpmProcessDefinitionConvertImpl.class new file mode 100644 index 00000000..52e63a5e Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/definition/BpmProcessDefinitionConvertImpl.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/definition/BpmTaskAssignRuleConvert.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/definition/BpmTaskAssignRuleConvert.class new file mode 100644 index 00000000..18322332 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/definition/BpmTaskAssignRuleConvert.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/definition/BpmTaskAssignRuleConvertImpl.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/definition/BpmTaskAssignRuleConvertImpl.class new file mode 100644 index 00000000..34835cbf Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/definition/BpmTaskAssignRuleConvertImpl.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/definition/BpmUserGroupConvert.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/definition/BpmUserGroupConvert.class new file mode 100644 index 00000000..13aaf1ce Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/definition/BpmUserGroupConvert.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/definition/BpmUserGroupConvertImpl.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/definition/BpmUserGroupConvertImpl.class new file mode 100644 index 00000000..cbc11042 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/definition/BpmUserGroupConvertImpl.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/message/BpmMessageConvert.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/message/BpmMessageConvert.class new file mode 100644 index 00000000..cdfb7199 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/message/BpmMessageConvert.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/message/BpmMessageConvertImpl.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/message/BpmMessageConvertImpl.class new file mode 100644 index 00000000..536e86b0 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/message/BpmMessageConvertImpl.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/oa/BpmOALeaveConvert.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/oa/BpmOALeaveConvert.class new file mode 100644 index 00000000..fb6b2dc7 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/oa/BpmOALeaveConvert.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/oa/BpmOALeaveConvertImpl.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/oa/BpmOALeaveConvertImpl.class new file mode 100644 index 00000000..59f23f76 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/oa/BpmOALeaveConvertImpl.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/task/BpmActivityConvert.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/task/BpmActivityConvert.class new file mode 100644 index 00000000..68cb2cbc Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/task/BpmActivityConvert.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/task/BpmActivityConvertImpl.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/task/BpmActivityConvertImpl.class new file mode 100644 index 00000000..98e49e06 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/task/BpmActivityConvertImpl.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/task/BpmProcessInstanceConvert.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/task/BpmProcessInstanceConvert.class new file mode 100644 index 00000000..22a7fbc5 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/task/BpmProcessInstanceConvert.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/task/BpmProcessInstanceConvertImpl.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/task/BpmProcessInstanceConvertImpl.class new file mode 100644 index 00000000..720eee3e Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/task/BpmProcessInstanceConvertImpl.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/task/BpmTaskConvert.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/task/BpmTaskConvert.class new file mode 100644 index 00000000..a9620fa1 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/task/BpmTaskConvert.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/task/BpmTaskConvertImpl.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/task/BpmTaskConvertImpl.class new file mode 100644 index 00000000..ae80ac57 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/task/BpmTaskConvertImpl.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/user/DeptConvert.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/user/DeptConvert.class new file mode 100644 index 00000000..e4af8261 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/user/DeptConvert.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/user/DeptConvertImpl.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/user/DeptConvertImpl.class new file mode 100644 index 00000000..7cada341 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/user/DeptConvertImpl.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/user/UserConvert.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/user/UserConvert.class new file mode 100644 index 00000000..a5e1e508 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/user/UserConvert.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/user/UserConvertImpl.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/user/UserConvertImpl.class new file mode 100644 index 00000000..f20f6ddf Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/convert/user/UserConvertImpl.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/enums/DictTypeConstants.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/enums/DictTypeConstants.class new file mode 100644 index 00000000..b5f040e3 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/enums/DictTypeConstants.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/enums/ErrorCodeConstants.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/enums/ErrorCodeConstants.class new file mode 100644 index 00000000..0a0cf6e2 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/enums/ErrorCodeConstants.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/enums/definition/BpmModelFormTypeEnum.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/enums/definition/BpmModelFormTypeEnum.class new file mode 100644 index 00000000..62fdd3f4 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/enums/definition/BpmModelFormTypeEnum.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/enums/definition/BpmTaskAssignRuleTypeEnum.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/enums/definition/BpmTaskAssignRuleTypeEnum.class new file mode 100644 index 00000000..9ed2f9f3 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/enums/definition/BpmTaskAssignRuleTypeEnum.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/enums/definition/BpmTaskRuleScriptEnum.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/enums/definition/BpmTaskRuleScriptEnum.class new file mode 100644 index 00000000..f5d0ed32 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/enums/definition/BpmTaskRuleScriptEnum.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/enums/message/BpmMessageEnum.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/enums/message/BpmMessageEnum.class new file mode 100644 index 00000000..8ff8d219 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/enums/message/BpmMessageEnum.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/enums/task/BpmProcessInstanceDeleteReasonEnum.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/enums/task/BpmProcessInstanceDeleteReasonEnum.class new file mode 100644 index 00000000..f406d74e Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/enums/task/BpmProcessInstanceDeleteReasonEnum.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/enums/task/BpmProcessInstanceResultEnum.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/enums/task/BpmProcessInstanceResultEnum.class new file mode 100644 index 00000000..3243ad21 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/enums/task/BpmProcessInstanceResultEnum.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/enums/task/BpmProcessInstanceStatusEnum.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/enums/task/BpmProcessInstanceStatusEnum.class new file mode 100644 index 00000000..67ab31df Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/enums/task/BpmProcessInstanceStatusEnum.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/enums/user/ErrorCodeConstants.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/enums/user/ErrorCodeConstants.class new file mode 100644 index 00000000..ff0ce6e6 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/enums/user/ErrorCodeConstants.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/enums/user/SysErrorCodeConstants.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/enums/user/SysErrorCodeConstants.class new file mode 100644 index 00000000..8bff78ce Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/enums/user/SysErrorCodeConstants.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/utils/FlowableUtils.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/utils/FlowableUtils.class new file mode 100644 index 00000000..c06df7fa Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/utils/FlowableUtils.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/web/FlowableWebFilter.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/web/FlowableWebFilter.class new file mode 100644 index 00000000..ca064505 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/core/web/FlowableWebFilter.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/dto/definition/BpmFormFieldRespDTO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/dto/definition/BpmFormFieldRespDTO.class new file mode 100644 index 00000000..9bd9fdf2 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/dto/definition/BpmFormFieldRespDTO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/dto/definition/BpmModelMetaInfoRespDTO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/dto/definition/BpmModelMetaInfoRespDTO.class new file mode 100644 index 00000000..726735aa Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/dto/definition/BpmModelMetaInfoRespDTO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/dto/definition/BpmProcessDefinitionCreateReqDTO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/dto/definition/BpmProcessDefinitionCreateReqDTO.class new file mode 100644 index 00000000..f6572b10 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/dto/definition/BpmProcessDefinitionCreateReqDTO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/dto/message/BpmMessageSendWhenProcessInstanceApproveReqDTO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/dto/message/BpmMessageSendWhenProcessInstanceApproveReqDTO.class new file mode 100644 index 00000000..3ff31503 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/dto/message/BpmMessageSendWhenProcessInstanceApproveReqDTO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/dto/message/BpmMessageSendWhenProcessInstanceRejectReqDTO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/dto/message/BpmMessageSendWhenProcessInstanceRejectReqDTO.class new file mode 100644 index 00000000..04ff0ec2 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/dto/message/BpmMessageSendWhenProcessInstanceRejectReqDTO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/dto/message/BpmMessageSendWhenTaskCreatedReqDTO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/dto/message/BpmMessageSendWhenTaskCreatedReqDTO.class new file mode 100644 index 00000000..a27d8618 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/dto/message/BpmMessageSendWhenTaskCreatedReqDTO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/dto/send/SmsSendSingleToUserReqDTO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/dto/send/SmsSendSingleToUserReqDTO.class new file mode 100644 index 00000000..4dc3aff3 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/dto/send/SmsSendSingleToUserReqDTO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/dto/task/BpmProcessInstanceCreateReqDTO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/dto/task/BpmProcessInstanceCreateReqDTO.class new file mode 100644 index 00000000..ace10aef Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/dto/task/BpmProcessInstanceCreateReqDTO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/dto/user/AdminUserRespDTO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/dto/user/AdminUserRespDTO.class new file mode 100644 index 00000000..df3fa29d Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/dto/user/AdminUserRespDTO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/dto/user/DeptRespDTO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/dto/user/DeptRespDTO.class new file mode 100644 index 00000000..3d03e3eb Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/dto/user/DeptRespDTO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/definition/BpmFormDO$BpmFormDOBuilder.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/definition/BpmFormDO$BpmFormDOBuilder.class new file mode 100644 index 00000000..322d046e Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/definition/BpmFormDO$BpmFormDOBuilder.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/definition/BpmFormDO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/definition/BpmFormDO.class new file mode 100644 index 00000000..b5e66e16 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/definition/BpmFormDO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/definition/BpmProcessDefinitionExtDO$BpmProcessDefinitionExtDOBuilder.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/definition/BpmProcessDefinitionExtDO$BpmProcessDefinitionExtDOBuilder.class new file mode 100644 index 00000000..9e947f2f Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/definition/BpmProcessDefinitionExtDO$BpmProcessDefinitionExtDOBuilder.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/definition/BpmProcessDefinitionExtDO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/definition/BpmProcessDefinitionExtDO.class new file mode 100644 index 00000000..928155a1 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/definition/BpmProcessDefinitionExtDO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/definition/BpmTaskAssignRuleDO$BpmTaskAssignRuleDOBuilder.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/definition/BpmTaskAssignRuleDO$BpmTaskAssignRuleDOBuilder.class new file mode 100644 index 00000000..f5630500 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/definition/BpmTaskAssignRuleDO$BpmTaskAssignRuleDOBuilder.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/definition/BpmTaskAssignRuleDO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/definition/BpmTaskAssignRuleDO.class new file mode 100644 index 00000000..0d1a6187 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/definition/BpmTaskAssignRuleDO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/definition/BpmTaskMessageRuleDO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/definition/BpmTaskMessageRuleDO.class new file mode 100644 index 00000000..613a4548 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/definition/BpmTaskMessageRuleDO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/definition/BpmUserGroupDO$BpmUserGroupDOBuilder.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/definition/BpmUserGroupDO$BpmUserGroupDOBuilder.class new file mode 100644 index 00000000..7dc486ad Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/definition/BpmUserGroupDO$BpmUserGroupDOBuilder.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/definition/BpmUserGroupDO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/definition/BpmUserGroupDO.class new file mode 100644 index 00000000..de037933 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/definition/BpmUserGroupDO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/oa/BpmOALeaveDO$BpmOALeaveDOBuilder.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/oa/BpmOALeaveDO$BpmOALeaveDOBuilder.class new file mode 100644 index 00000000..70d70d14 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/oa/BpmOALeaveDO$BpmOALeaveDOBuilder.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/oa/BpmOALeaveDO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/oa/BpmOALeaveDO.class new file mode 100644 index 00000000..8592ba3e Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/oa/BpmOALeaveDO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/task/BpmActivityDO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/task/BpmActivityDO.class new file mode 100644 index 00000000..38f1e1a5 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/task/BpmActivityDO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/task/BpmProcessInstanceExtDO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/task/BpmProcessInstanceExtDO.class new file mode 100644 index 00000000..30b865b3 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/task/BpmProcessInstanceExtDO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/task/BpmTaskExtDO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/task/BpmTaskExtDO.class new file mode 100644 index 00000000..305562de Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/entity/task/BpmTaskExtDO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/activity/BpmActivityRespVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/activity/BpmActivityRespVO.class new file mode 100644 index 00000000..cf9e68be Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/activity/BpmActivityRespVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/form/BpmFormBaseVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/form/BpmFormBaseVO.class new file mode 100644 index 00000000..2a89d88a Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/form/BpmFormBaseVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/form/BpmFormCreateReqVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/form/BpmFormCreateReqVO.class new file mode 100644 index 00000000..7a46751d Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/form/BpmFormCreateReqVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/form/BpmFormPageReqVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/form/BpmFormPageReqVO.class new file mode 100644 index 00000000..5b5edf3a Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/form/BpmFormPageReqVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/form/BpmFormRespVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/form/BpmFormRespVO.class new file mode 100644 index 00000000..58b97e30 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/form/BpmFormRespVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/form/BpmFormSimpleRespVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/form/BpmFormSimpleRespVO.class new file mode 100644 index 00000000..040952ff Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/form/BpmFormSimpleRespVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/form/BpmFormUpdateReqVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/form/BpmFormUpdateReqVO.class new file mode 100644 index 00000000..1f2ff1ae Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/form/BpmFormUpdateReqVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/group/BpmUserGroupBaseVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/group/BpmUserGroupBaseVO.class new file mode 100644 index 00000000..d370c987 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/group/BpmUserGroupBaseVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/group/BpmUserGroupCreateReqVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/group/BpmUserGroupCreateReqVO.class new file mode 100644 index 00000000..89c094c3 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/group/BpmUserGroupCreateReqVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/group/BpmUserGroupPageReqVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/group/BpmUserGroupPageReqVO.class new file mode 100644 index 00000000..0d6f9259 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/group/BpmUserGroupPageReqVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/group/BpmUserGroupRespVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/group/BpmUserGroupRespVO.class new file mode 100644 index 00000000..8c3e6d7d Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/group/BpmUserGroupRespVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/group/BpmUserGroupSimpleRespVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/group/BpmUserGroupSimpleRespVO.class new file mode 100644 index 00000000..f9e3bd97 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/group/BpmUserGroupSimpleRespVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/group/BpmUserGroupUpdateReqVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/group/BpmUserGroupUpdateReqVO.class new file mode 100644 index 00000000..121324b2 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/group/BpmUserGroupUpdateReqVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstanceCancelReqVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstanceCancelReqVO.class new file mode 100644 index 00000000..fdcc56e1 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstanceCancelReqVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstanceCreateReqVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstanceCreateReqVO.class new file mode 100644 index 00000000..a1f1e87e Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstanceCreateReqVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstanceMyPageReqVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstanceMyPageReqVO.class new file mode 100644 index 00000000..a478628f Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstanceMyPageReqVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstancePageItemRespVO$Task.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstancePageItemRespVO$Task.class new file mode 100644 index 00000000..3c5d4f26 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstancePageItemRespVO$Task.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstancePageItemRespVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstancePageItemRespVO.class new file mode 100644 index 00000000..a4d720f6 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstancePageItemRespVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstanceRespVO$ProcessDefinition.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstanceRespVO$ProcessDefinition.class new file mode 100644 index 00000000..f12ec1c5 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstanceRespVO$ProcessDefinition.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstanceRespVO$User.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstanceRespVO$User.class new file mode 100644 index 00000000..7890a7a3 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstanceRespVO$User.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstanceRespVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstanceRespVO.class new file mode 100644 index 00000000..4753f8a5 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/instance/BpmProcessInstanceRespVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/model/BpmModeImportReqVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/model/BpmModeImportReqVO.class new file mode 100644 index 00000000..9ca89cad Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/model/BpmModeImportReqVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/model/BpmModelBaseVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/model/BpmModelBaseVO.class new file mode 100644 index 00000000..75cb544a Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/model/BpmModelBaseVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/model/BpmModelCreateReqVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/model/BpmModelCreateReqVO.class new file mode 100644 index 00000000..d3402f82 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/model/BpmModelCreateReqVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/model/BpmModelPageItemRespVO$ProcessDefinition.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/model/BpmModelPageItemRespVO$ProcessDefinition.class new file mode 100644 index 00000000..0abe38fd Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/model/BpmModelPageItemRespVO$ProcessDefinition.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/model/BpmModelPageItemRespVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/model/BpmModelPageItemRespVO.class new file mode 100644 index 00000000..dde1987d Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/model/BpmModelPageItemRespVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/model/BpmModelPageReqVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/model/BpmModelPageReqVO.class new file mode 100644 index 00000000..514059c5 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/model/BpmModelPageReqVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/model/BpmModelRespVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/model/BpmModelRespVO.class new file mode 100644 index 00000000..a8f5376e Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/model/BpmModelRespVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/model/BpmModelUpdateReqVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/model/BpmModelUpdateReqVO.class new file mode 100644 index 00000000..d9b907a6 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/model/BpmModelUpdateReqVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/model/BpmModelUpdateStateReqVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/model/BpmModelUpdateStateReqVO.class new file mode 100644 index 00000000..1d0bc094 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/model/BpmModelUpdateStateReqVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/oa/BpmOALeaveBaseVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/oa/BpmOALeaveBaseVO.class new file mode 100644 index 00000000..0164929f Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/oa/BpmOALeaveBaseVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/oa/BpmOALeaveCreateReqVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/oa/BpmOALeaveCreateReqVO.class new file mode 100644 index 00000000..f65cef38 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/oa/BpmOALeaveCreateReqVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/oa/BpmOALeavePageReqVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/oa/BpmOALeavePageReqVO.class new file mode 100644 index 00000000..c761a07d Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/oa/BpmOALeavePageReqVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/oa/BpmOALeaveRespVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/oa/BpmOALeaveRespVO.class new file mode 100644 index 00000000..5c4507e1 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/oa/BpmOALeaveRespVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/process/BpmProcessDefinitionListReqVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/process/BpmProcessDefinitionListReqVO.class new file mode 100644 index 00000000..7883e199 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/process/BpmProcessDefinitionListReqVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/process/BpmProcessDefinitionPageItemRespVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/process/BpmProcessDefinitionPageItemRespVO.class new file mode 100644 index 00000000..4db27ad6 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/process/BpmProcessDefinitionPageItemRespVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/process/BpmProcessDefinitionPageReqVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/process/BpmProcessDefinitionPageReqVO.class new file mode 100644 index 00000000..824dba62 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/process/BpmProcessDefinitionPageReqVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/process/BpmProcessDefinitionRespVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/process/BpmProcessDefinitionRespVO.class new file mode 100644 index 00000000..102f23d1 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/process/BpmProcessDefinitionRespVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/rule/BpmTaskAssignRuleBaseVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/rule/BpmTaskAssignRuleBaseVO.class new file mode 100644 index 00000000..d6365774 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/rule/BpmTaskAssignRuleBaseVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/rule/BpmTaskAssignRuleCreateReqVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/rule/BpmTaskAssignRuleCreateReqVO.class new file mode 100644 index 00000000..1bcea20e Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/rule/BpmTaskAssignRuleCreateReqVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/rule/BpmTaskAssignRuleRespVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/rule/BpmTaskAssignRuleRespVO.class new file mode 100644 index 00000000..e78049d5 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/rule/BpmTaskAssignRuleRespVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/rule/BpmTaskAssignRuleUpdateReqVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/rule/BpmTaskAssignRuleUpdateReqVO.class new file mode 100644 index 00000000..156b8420 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/rule/BpmTaskAssignRuleUpdateReqVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/BackTaskVo.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/BackTaskVo.class new file mode 100644 index 00000000..ca840806 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/BackTaskVo.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/BpmTaskApproveReqVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/BpmTaskApproveReqVO.class new file mode 100644 index 00000000..46b86a27 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/BpmTaskApproveReqVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/BpmTaskDonePageItemRespVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/BpmTaskDonePageItemRespVO.class new file mode 100644 index 00000000..e722b358 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/BpmTaskDonePageItemRespVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/BpmTaskDonePageReqVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/BpmTaskDonePageReqVO.class new file mode 100644 index 00000000..cd72431a Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/BpmTaskDonePageReqVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/BpmTaskRejectReqVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/BpmTaskRejectReqVO.class new file mode 100644 index 00000000..0c0dada0 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/BpmTaskRejectReqVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/BpmTaskRespVO$User.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/BpmTaskRespVO$User.class new file mode 100644 index 00000000..4096c978 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/BpmTaskRespVO$User.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/BpmTaskRespVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/BpmTaskRespVO.class new file mode 100644 index 00000000..dde45a9a Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/BpmTaskRespVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/BpmTaskTodoPageItemRespVO$ProcessInstance.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/BpmTaskTodoPageItemRespVO$ProcessInstance.class new file mode 100644 index 00000000..50cdae00 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/BpmTaskTodoPageItemRespVO$ProcessInstance.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/BpmTaskTodoPageItemRespVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/BpmTaskTodoPageItemRespVO.class new file mode 100644 index 00000000..e31f9483 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/BpmTaskTodoPageItemRespVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/BpmTaskTodoPageReqVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/BpmTaskTodoPageReqVO.class new file mode 100644 index 00000000..bf388691 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/BpmTaskTodoPageReqVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/BpmTaskUpdateAssigneeReqVO.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/BpmTaskUpdateAssigneeReqVO.class new file mode 100644 index 00000000..2998d055 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/BpmTaskUpdateAssigneeReqVO.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/FlowNodeVo.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/FlowNodeVo.class new file mode 100644 index 00000000..e11271b9 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/domain/vo/task/FlowNodeVo.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/bpm/config/BpmCommonConfiguration.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/bpm/config/BpmCommonConfiguration.class new file mode 100644 index 00000000..10200707 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/bpm/config/BpmCommonConfiguration.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/bpm/core/event/BpmProcessInstanceResultEvent.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/bpm/core/event/BpmProcessInstanceResultEvent.class new file mode 100644 index 00000000..b0df093d Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/bpm/core/event/BpmProcessInstanceResultEvent.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/bpm/core/event/BpmProcessInstanceResultEventListener.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/bpm/core/event/BpmProcessInstanceResultEventListener.class new file mode 100644 index 00000000..5c9170f0 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/bpm/core/event/BpmProcessInstanceResultEventListener.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/bpm/core/event/BpmProcessInstanceResultEventPublisher.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/bpm/core/event/BpmProcessInstanceResultEventPublisher.class new file mode 100644 index 00000000..b5edba61 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/bpm/core/event/BpmProcessInstanceResultEventPublisher.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/flowable/config/BpmFlowableConfiguration.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/flowable/config/BpmFlowableConfiguration.class new file mode 100644 index 00000000..4a8f9d9b Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/flowable/config/BpmFlowableConfiguration.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/flowable/core/behavior/BpmActivityBehaviorFactory.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/flowable/core/behavior/BpmActivityBehaviorFactory.class new file mode 100644 index 00000000..b11bca67 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/flowable/core/behavior/BpmActivityBehaviorFactory.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.class new file mode 100644 index 00000000..389b6c8f Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/flowable/core/behavior/BpmUserTaskActivityBehavior.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/flowable/core/behavior/BpmUserTaskActivityBehavior.class new file mode 100644 index 00000000..1e2cd715 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/flowable/core/behavior/BpmUserTaskActivityBehavior.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/flowable/core/behavior/script/BpmTaskAssignScript.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/flowable/core/behavior/script/BpmTaskAssignScript.class new file mode 100644 index 00000000..5c789ea6 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/flowable/core/behavior/script/BpmTaskAssignScript.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderAbstractScript.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderAbstractScript.class new file mode 100644 index 00000000..9bc999c5 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderAbstractScript.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderX1Script.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderX1Script.class new file mode 100644 index 00000000..ee374c74 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderX1Script.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderX2Script.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderX2Script.class new file mode 100644 index 00000000..e8483c39 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderX2Script.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/flowable/core/behavior/script/impl/BpmTaskAssignStartUserScript.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/flowable/core/behavior/script/impl/BpmTaskAssignStartUserScript.class new file mode 100644 index 00000000..ae2db338 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/flowable/core/behavior/script/impl/BpmTaskAssignStartUserScript.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/flowable/core/listener/BpmProcessInstanceEventListener.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/flowable/core/listener/BpmProcessInstanceEventListener.class new file mode 100644 index 00000000..4c9351f3 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/flowable/core/listener/BpmProcessInstanceEventListener.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/flowable/core/listener/BpmTaskEventListener.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/flowable/core/listener/BpmTaskEventListener.class new file mode 100644 index 00000000..a0c178e1 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/framework/flowable/core/listener/BpmTaskEventListener.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/mapper/definition/BpmFormMapper.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/mapper/definition/BpmFormMapper.class new file mode 100644 index 00000000..35d415dc Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/mapper/definition/BpmFormMapper.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/mapper/definition/BpmProcessDefinitionExtMapper.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/mapper/definition/BpmProcessDefinitionExtMapper.class new file mode 100644 index 00000000..925c2e30 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/mapper/definition/BpmProcessDefinitionExtMapper.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/mapper/definition/BpmTaskAssignRuleMapper.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/mapper/definition/BpmTaskAssignRuleMapper.class new file mode 100644 index 00000000..60e4af8c Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/mapper/definition/BpmTaskAssignRuleMapper.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/mapper/definition/BpmUserGroupMapper.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/mapper/definition/BpmUserGroupMapper.class new file mode 100644 index 00000000..272bf70b Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/mapper/definition/BpmUserGroupMapper.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/mapper/oa/BpmOALeaveMapper.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/mapper/oa/BpmOALeaveMapper.class new file mode 100644 index 00000000..0eb55813 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/mapper/oa/BpmOALeaveMapper.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/mapper/task/BpmProcessInstanceExtMapper.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/mapper/task/BpmProcessInstanceExtMapper.class new file mode 100644 index 00000000..6d6f84cc Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/mapper/task/BpmProcessInstanceExtMapper.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/mapper/task/BpmTaskExtMapper.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/mapper/task/BpmTaskExtMapper.class new file mode 100644 index 00000000..5f468dba Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/mapper/task/BpmTaskExtMapper.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/BpmFormService.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/BpmFormService.class new file mode 100644 index 00000000..43caeae9 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/BpmFormService.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/BpmModelService.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/BpmModelService.class new file mode 100644 index 00000000..635fc96b Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/BpmModelService.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/BpmProcessDefinitionService.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/BpmProcessDefinitionService.class new file mode 100644 index 00000000..15e1f110 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/BpmProcessDefinitionService.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/BpmProcessInstanceApi.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/BpmProcessInstanceApi.class new file mode 100644 index 00000000..4fcabebd Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/BpmProcessInstanceApi.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/BpmTaskAssignRuleService.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/BpmTaskAssignRuleService.class new file mode 100644 index 00000000..ada3fbb8 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/BpmTaskAssignRuleService.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/BpmUserGroupService.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/BpmUserGroupService.class new file mode 100644 index 00000000..e916d02e Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/BpmUserGroupService.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/impl/BpmFormServiceImpl.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/impl/BpmFormServiceImpl.class new file mode 100644 index 00000000..5b93bedc Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/impl/BpmFormServiceImpl.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/impl/BpmModelServiceImpl.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/impl/BpmModelServiceImpl.class new file mode 100644 index 00000000..ce894588 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/impl/BpmModelServiceImpl.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/impl/BpmProcessDefinitionServiceImpl.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/impl/BpmProcessDefinitionServiceImpl.class new file mode 100644 index 00000000..e6cea37b Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/impl/BpmProcessDefinitionServiceImpl.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/impl/BpmProcessInstanceApiImpl.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/impl/BpmProcessInstanceApiImpl.class new file mode 100644 index 00000000..66e435aa Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/impl/BpmProcessInstanceApiImpl.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/impl/BpmTaskAssignRuleServiceImpl.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/impl/BpmTaskAssignRuleServiceImpl.class new file mode 100644 index 00000000..9abf2eca Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/impl/BpmTaskAssignRuleServiceImpl.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/impl/BpmUserGroupServiceImpl.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/impl/BpmUserGroupServiceImpl.class new file mode 100644 index 00000000..2dccd1c2 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/definition/impl/BpmUserGroupServiceImpl.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/message/BpmMessageService.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/message/BpmMessageService.class new file mode 100644 index 00000000..5cd873eb Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/message/BpmMessageService.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/message/impl/BpmMessageServiceImpl.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/message/impl/BpmMessageServiceImpl.class new file mode 100644 index 00000000..ea898380 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/message/impl/BpmMessageServiceImpl.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/oa/BpmOALeaveService.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/oa/BpmOALeaveService.class new file mode 100644 index 00000000..82c5fa1b Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/oa/BpmOALeaveService.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/oa/BpmOALeaveServiceImpl.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/oa/BpmOALeaveServiceImpl.class new file mode 100644 index 00000000..4e601a54 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/oa/BpmOALeaveServiceImpl.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/oa/listener/BpmOALeaveResultListener.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/oa/listener/BpmOALeaveResultListener.class new file mode 100644 index 00000000..f7c906cd Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/oa/listener/BpmOALeaveResultListener.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/task/BpmActivityService.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/task/BpmActivityService.class new file mode 100644 index 00000000..ccbc19b9 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/task/BpmActivityService.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/task/BpmProcessInstanceService.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/task/BpmProcessInstanceService.class new file mode 100644 index 00000000..df8d1765 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/task/BpmProcessInstanceService.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/task/BpmTaskService.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/task/BpmTaskService.class new file mode 100644 index 00000000..7908816d Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/task/BpmTaskService.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/task/impl/BpmActivityServiceImpl.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/task/impl/BpmActivityServiceImpl.class new file mode 100644 index 00000000..839a5caf Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/task/impl/BpmActivityServiceImpl.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/task/impl/BpmProcessInstanceServiceImpl.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/task/impl/BpmProcessInstanceServiceImpl.class new file mode 100644 index 00000000..a3836031 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/task/impl/BpmProcessInstanceServiceImpl.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/task/impl/BpmTaskServiceImpl$1.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/task/impl/BpmTaskServiceImpl$1.class new file mode 100644 index 00000000..f2d10002 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/task/impl/BpmTaskServiceImpl$1.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/task/impl/BpmTaskServiceImpl$2.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/task/impl/BpmTaskServiceImpl$2.class new file mode 100644 index 00000000..31deeb36 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/task/impl/BpmTaskServiceImpl$2.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/task/impl/BpmTaskServiceImpl.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/task/impl/BpmTaskServiceImpl.class new file mode 100644 index 00000000..ba30f075 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/task/impl/BpmTaskServiceImpl.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/AdminUserApi.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/AdminUserApi.class new file mode 100644 index 00000000..16cb62a1 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/AdminUserApi.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/DeptApi.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/DeptApi.class new file mode 100644 index 00000000..727469cc Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/DeptApi.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/DictDataApi.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/DictDataApi.class new file mode 100644 index 00000000..e6797650 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/DictDataApi.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/PermissionApi.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/PermissionApi.class new file mode 100644 index 00000000..da045907 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/PermissionApi.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/PostApi.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/PostApi.class new file mode 100644 index 00000000..0c6e477e Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/PostApi.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/RoleApi.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/RoleApi.class new file mode 100644 index 00000000..f82b2c02 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/RoleApi.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/impl/AdminUserApiImpl.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/impl/AdminUserApiImpl.class new file mode 100644 index 00000000..f2b8b2fd Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/impl/AdminUserApiImpl.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/impl/DeptApiImpl.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/impl/DeptApiImpl.class new file mode 100644 index 00000000..673fb3df Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/impl/DeptApiImpl.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/impl/DictDataApiImpl.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/impl/DictDataApiImpl.class new file mode 100644 index 00000000..1eadf238 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/impl/DictDataApiImpl.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/impl/PermissionApiImpl.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/impl/PermissionApiImpl.class new file mode 100644 index 00000000..9bc3477a Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/impl/PermissionApiImpl.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/impl/PostApiImpl.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/impl/PostApiImpl.class new file mode 100644 index 00000000..a35c7bbf Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/impl/PostApiImpl.class differ diff --git a/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/impl/RoleApiImpl.class b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/impl/RoleApiImpl.class new file mode 100644 index 00000000..2c2a9993 Binary files /dev/null and b/ruoyi-flowable/target/classes/com/ruoyi/flowable/service/user/impl/RoleApiImpl.class differ diff --git a/ruoyi-flowable/target/classes/mapper/flowable/BpmOALeaveMapper.xml b/ruoyi-flowable/target/classes/mapper/flowable/BpmOALeaveMapper.xml new file mode 100644 index 00000000..8d88b183 --- /dev/null +++ b/ruoyi-flowable/target/classes/mapper/flowable/BpmOALeaveMapper.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + select id, user_id, type, reason, start_time, end_time, day, result, process_instance_id, creator, create_time, updater, update_time, deleted, tenant_id from bpm_oa_leave + + + + + diff --git a/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/definition/BpmFormConvertImpl.java b/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/definition/BpmFormConvertImpl.java new file mode 100644 index 00000000..ff68c62c --- /dev/null +++ b/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/definition/BpmFormConvertImpl.java @@ -0,0 +1,137 @@ +package com.ruoyi.flowable.convert.definition; + +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.flowable.domain.entity.definition.BpmFormDO; +import com.ruoyi.flowable.domain.entity.definition.BpmFormDO.BpmFormDOBuilder; +import com.ruoyi.flowable.domain.vo.form.BpmFormCreateReqVO; +import com.ruoyi.flowable.domain.vo.form.BpmFormRespVO; +import com.ruoyi.flowable.domain.vo.form.BpmFormSimpleRespVO; +import com.ruoyi.flowable.domain.vo.form.BpmFormUpdateReqVO; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2023-07-11T22:37:16+0800", + comments = "version: 1.4.1.Final, compiler: javac, environment: Java 1.8.0_91 (Oracle Corporation)" +) +public class BpmFormConvertImpl implements BpmFormConvert { + + @Override + public BpmFormDO convert(BpmFormCreateReqVO bean) { + if ( bean == null ) { + return null; + } + + BpmFormDOBuilder bpmFormDO = BpmFormDO.builder(); + + bpmFormDO.name( bean.getName() ); + bpmFormDO.status( bean.getStatus() ); + bpmFormDO.conf( bean.getConf() ); + List list = bean.getFields(); + if ( list != null ) { + bpmFormDO.fields( new ArrayList( list ) ); + } + bpmFormDO.remark( bean.getRemark() ); + + return bpmFormDO.build(); + } + + @Override + public BpmFormDO convert(BpmFormUpdateReqVO bean) { + if ( bean == null ) { + return null; + } + + BpmFormDOBuilder bpmFormDO = BpmFormDO.builder(); + + bpmFormDO.id( bean.getId() ); + bpmFormDO.name( bean.getName() ); + bpmFormDO.status( bean.getStatus() ); + bpmFormDO.conf( bean.getConf() ); + List list = bean.getFields(); + if ( list != null ) { + bpmFormDO.fields( new ArrayList( list ) ); + } + bpmFormDO.remark( bean.getRemark() ); + + return bpmFormDO.build(); + } + + @Override + public BpmFormRespVO convert(BpmFormDO bean) { + if ( bean == null ) { + return null; + } + + BpmFormRespVO bpmFormRespVO = new BpmFormRespVO(); + + bpmFormRespVO.setName( bean.getName() ); + bpmFormRespVO.setStatus( bean.getStatus() ); + bpmFormRespVO.setRemark( bean.getRemark() ); + bpmFormRespVO.setId( bean.getId() ); + bpmFormRespVO.setConf( bean.getConf() ); + List list = bean.getFields(); + if ( list != null ) { + bpmFormRespVO.setFields( new ArrayList( list ) ); + } + bpmFormRespVO.setCreateTime( bean.getCreateTime() ); + + return bpmFormRespVO; + } + + @Override + public List convertList2(List list) { + if ( list == null ) { + return null; + } + + List list1 = new ArrayList( list.size() ); + for ( BpmFormDO bpmFormDO : list ) { + list1.add( bpmFormDOToBpmFormSimpleRespVO( bpmFormDO ) ); + } + + return list1; + } + + @Override + public PageResult convertPage(PageResult page) { + if ( page == null ) { + return null; + } + + PageResult pageResult = new PageResult(); + + pageResult.setList( bpmFormDOListToBpmFormRespVOList( page.getList() ) ); + pageResult.setTotal( page.getTotal() ); + + return pageResult; + } + + protected BpmFormSimpleRespVO bpmFormDOToBpmFormSimpleRespVO(BpmFormDO bpmFormDO) { + if ( bpmFormDO == null ) { + return null; + } + + BpmFormSimpleRespVO bpmFormSimpleRespVO = new BpmFormSimpleRespVO(); + + bpmFormSimpleRespVO.setId( bpmFormDO.getId() ); + bpmFormSimpleRespVO.setName( bpmFormDO.getName() ); + + return bpmFormSimpleRespVO; + } + + protected List bpmFormDOListToBpmFormRespVOList(List list) { + if ( list == null ) { + return null; + } + + List list1 = new ArrayList( list.size() ); + for ( BpmFormDO bpmFormDO : list ) { + list1.add( convert( bpmFormDO ) ); + } + + return list1; + } +} diff --git a/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/definition/BpmModelConvertImpl.java b/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/definition/BpmModelConvertImpl.java new file mode 100644 index 00000000..efb4177b --- /dev/null +++ b/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/definition/BpmModelConvertImpl.java @@ -0,0 +1,72 @@ +package com.ruoyi.flowable.convert.definition; + +import com.ruoyi.flowable.domain.dto.definition.BpmModelMetaInfoRespDTO; +import com.ruoyi.flowable.domain.dto.definition.BpmProcessDefinitionCreateReqDTO; +import com.ruoyi.flowable.domain.vo.model.BpmModeImportReqVO; +import com.ruoyi.flowable.domain.vo.model.BpmModelBaseVO; +import com.ruoyi.flowable.domain.vo.model.BpmModelCreateReqVO; +import javax.annotation.Generated; +import org.flowable.engine.repository.ProcessDefinition; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2023-07-11T22:37:16+0800", + comments = "version: 1.4.1.Final, compiler: javac, environment: Java 1.8.0_91 (Oracle Corporation)" +) +public class BpmModelConvertImpl implements BpmModelConvert { + + @Override + public BpmModelCreateReqVO convert(BpmModeImportReqVO bean) { + if ( bean == null ) { + return null; + } + + BpmModelCreateReqVO bpmModelCreateReqVO = new BpmModelCreateReqVO(); + + bpmModelCreateReqVO.setKey( bean.getKey() ); + bpmModelCreateReqVO.setName( bean.getName() ); + bpmModelCreateReqVO.setDescription( bean.getDescription() ); + + return bpmModelCreateReqVO; + } + + @Override + public void copyTo(BpmModelMetaInfoRespDTO from, BpmProcessDefinitionCreateReqDTO to) { + if ( from == null ) { + return; + } + + to.setDescription( from.getDescription() ); + to.setFormType( from.getFormType() ); + to.setFormId( from.getFormId() ); + to.setFormCustomCreatePath( from.getFormCustomCreatePath() ); + to.setFormCustomViewPath( from.getFormCustomViewPath() ); + } + + @Override + public void copyTo(BpmModelMetaInfoRespDTO from, BpmModelBaseVO to) { + if ( from == null ) { + return; + } + + to.setDescription( from.getDescription() ); + to.setFormType( from.getFormType() ); + to.setFormId( from.getFormId() ); + to.setFormCustomCreatePath( from.getFormCustomCreatePath() ); + to.setFormCustomViewPath( from.getFormCustomViewPath() ); + } + + @Override + public com.ruoyi.flowable.domain.vo.model.BpmModelPageItemRespVO.ProcessDefinition convert(ProcessDefinition bean) { + if ( bean == null ) { + return null; + } + + com.ruoyi.flowable.domain.vo.model.BpmModelPageItemRespVO.ProcessDefinition processDefinition = new com.ruoyi.flowable.domain.vo.model.BpmModelPageItemRespVO.ProcessDefinition(); + + processDefinition.setId( bean.getId() ); + processDefinition.setVersion( bean.getVersion() ); + + return processDefinition; + } +} diff --git a/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/definition/BpmProcessDefinitionConvertImpl.java b/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/definition/BpmProcessDefinitionConvertImpl.java new file mode 100644 index 00000000..ec9320a2 --- /dev/null +++ b/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/definition/BpmProcessDefinitionConvertImpl.java @@ -0,0 +1,107 @@ +package com.ruoyi.flowable.convert.definition; + +import com.ruoyi.flowable.domain.dto.definition.BpmProcessDefinitionCreateReqDTO; +import com.ruoyi.flowable.domain.entity.definition.BpmProcessDefinitionExtDO; +import com.ruoyi.flowable.domain.entity.definition.BpmProcessDefinitionExtDO.BpmProcessDefinitionExtDOBuilder; +import com.ruoyi.flowable.domain.vo.process.BpmProcessDefinitionPageItemRespVO; +import com.ruoyi.flowable.domain.vo.process.BpmProcessDefinitionRespVO; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Generated; +import org.flowable.engine.repository.ProcessDefinition; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2023-07-11T22:37:16+0800", + comments = "version: 1.4.1.Final, compiler: javac, environment: Java 1.8.0_91 (Oracle Corporation)" +) +public class BpmProcessDefinitionConvertImpl implements BpmProcessDefinitionConvert { + + @Override + public BpmProcessDefinitionPageItemRespVO convert(ProcessDefinition bean) { + if ( bean == null ) { + return null; + } + + BpmProcessDefinitionPageItemRespVO bpmProcessDefinitionPageItemRespVO = new BpmProcessDefinitionPageItemRespVO(); + + bpmProcessDefinitionPageItemRespVO.setId( bean.getId() ); + bpmProcessDefinitionPageItemRespVO.setVersion( bean.getVersion() ); + bpmProcessDefinitionPageItemRespVO.setName( bean.getName() ); + bpmProcessDefinitionPageItemRespVO.setDescription( bean.getDescription() ); + bpmProcessDefinitionPageItemRespVO.setCategory( bean.getCategory() ); + + return bpmProcessDefinitionPageItemRespVO; + } + + @Override + public BpmProcessDefinitionExtDO convert2(BpmProcessDefinitionCreateReqDTO bean) { + if ( bean == null ) { + return null; + } + + BpmProcessDefinitionExtDOBuilder bpmProcessDefinitionExtDO = BpmProcessDefinitionExtDO.builder(); + + bpmProcessDefinitionExtDO.modelId( bean.getModelId() ); + bpmProcessDefinitionExtDO.description( bean.getDescription() ); + bpmProcessDefinitionExtDO.formType( bean.getFormType() ); + bpmProcessDefinitionExtDO.formId( bean.getFormId() ); + bpmProcessDefinitionExtDO.formConf( bean.getFormConf() ); + List list = bean.getFormFields(); + if ( list != null ) { + bpmProcessDefinitionExtDO.formFields( new ArrayList( list ) ); + } + bpmProcessDefinitionExtDO.formCustomCreatePath( bean.getFormCustomCreatePath() ); + bpmProcessDefinitionExtDO.formCustomViewPath( bean.getFormCustomViewPath() ); + + return bpmProcessDefinitionExtDO.build(); + } + + @Override + public BpmProcessDefinitionRespVO convert3(ProcessDefinition bean) { + if ( bean == null ) { + return null; + } + + BpmProcessDefinitionRespVO bpmProcessDefinitionRespVO = new BpmProcessDefinitionRespVO(); + + bpmProcessDefinitionRespVO.setSuspensionState( convertSuspendedToSuspensionState( bean.isSuspended() ) ); + bpmProcessDefinitionRespVO.setId( bean.getId() ); + bpmProcessDefinitionRespVO.setVersion( bean.getVersion() ); + bpmProcessDefinitionRespVO.setName( bean.getName() ); + bpmProcessDefinitionRespVO.setDescription( bean.getDescription() ); + bpmProcessDefinitionRespVO.setCategory( bean.getCategory() ); + + return bpmProcessDefinitionRespVO; + } + + @Override + public void copyTo(BpmProcessDefinitionExtDO from, BpmProcessDefinitionRespVO to) { + if ( from == null ) { + return; + } + + to.setDescription( from.getDescription() ); + to.setFormType( from.getFormType() ); + to.setFormId( from.getFormId() ); + to.setFormConf( from.getFormConf() ); + if ( to.getFormFields() != null ) { + List list = from.getFormFields(); + if ( list != null ) { + to.getFormFields().clear(); + to.getFormFields().addAll( list ); + } + else { + to.setFormFields( null ); + } + } + else { + List list = from.getFormFields(); + if ( list != null ) { + to.setFormFields( new ArrayList( list ) ); + } + } + to.setFormCustomCreatePath( from.getFormCustomCreatePath() ); + to.setFormCustomViewPath( from.getFormCustomViewPath() ); + } +} diff --git a/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/definition/BpmTaskAssignRuleConvertImpl.java b/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/definition/BpmTaskAssignRuleConvertImpl.java new file mode 100644 index 00000000..87eea98a --- /dev/null +++ b/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/definition/BpmTaskAssignRuleConvertImpl.java @@ -0,0 +1,112 @@ +package com.ruoyi.flowable.convert.definition; + +import com.ruoyi.flowable.domain.entity.definition.BpmTaskAssignRuleDO; +import com.ruoyi.flowable.domain.entity.definition.BpmTaskAssignRuleDO.BpmTaskAssignRuleDOBuilder; +import com.ruoyi.flowable.domain.vo.rule.BpmTaskAssignRuleCreateReqVO; +import com.ruoyi.flowable.domain.vo.rule.BpmTaskAssignRuleRespVO; +import com.ruoyi.flowable.domain.vo.rule.BpmTaskAssignRuleUpdateReqVO; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import javax.annotation.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2023-07-11T22:37:16+0800", + comments = "version: 1.4.1.Final, compiler: javac, environment: Java 1.8.0_91 (Oracle Corporation)" +) +public class BpmTaskAssignRuleConvertImpl implements BpmTaskAssignRuleConvert { + + @Override + public BpmTaskAssignRuleRespVO convert(BpmTaskAssignRuleDO bean) { + if ( bean == null ) { + return null; + } + + BpmTaskAssignRuleRespVO bpmTaskAssignRuleRespVO = new BpmTaskAssignRuleRespVO(); + + bpmTaskAssignRuleRespVO.setType( bean.getType() ); + Set set = bean.getOptions(); + if ( set != null ) { + bpmTaskAssignRuleRespVO.setOptions( new HashSet( set ) ); + } + bpmTaskAssignRuleRespVO.setId( bean.getId() ); + bpmTaskAssignRuleRespVO.setModelId( bean.getModelId() ); + bpmTaskAssignRuleRespVO.setProcessDefinitionId( bean.getProcessDefinitionId() ); + bpmTaskAssignRuleRespVO.setTaskDefinitionKey( bean.getTaskDefinitionKey() ); + + return bpmTaskAssignRuleRespVO; + } + + @Override + public BpmTaskAssignRuleDO convert(BpmTaskAssignRuleCreateReqVO bean) { + if ( bean == null ) { + return null; + } + + BpmTaskAssignRuleDOBuilder bpmTaskAssignRuleDO = BpmTaskAssignRuleDO.builder(); + + bpmTaskAssignRuleDO.modelId( bean.getModelId() ); + bpmTaskAssignRuleDO.taskDefinitionKey( bean.getTaskDefinitionKey() ); + bpmTaskAssignRuleDO.type( bean.getType() ); + Set set = bean.getOptions(); + if ( set != null ) { + bpmTaskAssignRuleDO.options( new HashSet( set ) ); + } + + return bpmTaskAssignRuleDO.build(); + } + + @Override + public BpmTaskAssignRuleDO convert(BpmTaskAssignRuleUpdateReqVO bean) { + if ( bean == null ) { + return null; + } + + BpmTaskAssignRuleDOBuilder bpmTaskAssignRuleDO = BpmTaskAssignRuleDO.builder(); + + bpmTaskAssignRuleDO.id( bean.getId() ); + bpmTaskAssignRuleDO.type( bean.getType() ); + Set set = bean.getOptions(); + if ( set != null ) { + bpmTaskAssignRuleDO.options( new HashSet( set ) ); + } + + return bpmTaskAssignRuleDO.build(); + } + + @Override + public List convertList2(List list) { + if ( list == null ) { + return null; + } + + List list1 = new ArrayList( list.size() ); + for ( BpmTaskAssignRuleRespVO bpmTaskAssignRuleRespVO : list ) { + list1.add( bpmTaskAssignRuleRespVOToBpmTaskAssignRuleDO( bpmTaskAssignRuleRespVO ) ); + } + + return list1; + } + + protected BpmTaskAssignRuleDO bpmTaskAssignRuleRespVOToBpmTaskAssignRuleDO(BpmTaskAssignRuleRespVO bpmTaskAssignRuleRespVO) { + if ( bpmTaskAssignRuleRespVO == null ) { + return null; + } + + BpmTaskAssignRuleDOBuilder bpmTaskAssignRuleDO = BpmTaskAssignRuleDO.builder(); + + bpmTaskAssignRuleDO.id( bpmTaskAssignRuleRespVO.getId() ); + bpmTaskAssignRuleDO.modelId( bpmTaskAssignRuleRespVO.getModelId() ); + bpmTaskAssignRuleDO.processDefinitionId( bpmTaskAssignRuleRespVO.getProcessDefinitionId() ); + bpmTaskAssignRuleDO.taskDefinitionKey( bpmTaskAssignRuleRespVO.getTaskDefinitionKey() ); + bpmTaskAssignRuleDO.type( bpmTaskAssignRuleRespVO.getType() ); + Set set = bpmTaskAssignRuleRespVO.getOptions(); + if ( set != null ) { + bpmTaskAssignRuleDO.options( new HashSet( set ) ); + } + + return bpmTaskAssignRuleDO.build(); + } +} diff --git a/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/definition/BpmUserGroupConvertImpl.java b/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/definition/BpmUserGroupConvertImpl.java new file mode 100644 index 00000000..11db940a --- /dev/null +++ b/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/definition/BpmUserGroupConvertImpl.java @@ -0,0 +1,123 @@ +package com.ruoyi.flowable.convert.definition; + +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.flowable.domain.entity.definition.BpmUserGroupDO; +import com.ruoyi.flowable.domain.entity.definition.BpmUserGroupDO.BpmUserGroupDOBuilder; +import com.ruoyi.flowable.domain.vo.group.BpmUserGroupCreateReqVO; +import com.ruoyi.flowable.domain.vo.group.BpmUserGroupRespVO; +import com.ruoyi.flowable.domain.vo.group.BpmUserGroupUpdateReqVO; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import javax.annotation.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2023-07-11T22:37:16+0800", + comments = "version: 1.4.1.Final, compiler: javac, environment: Java 1.8.0_91 (Oracle Corporation)" +) +public class BpmUserGroupConvertImpl implements BpmUserGroupConvert { + + @Override + public BpmUserGroupDO convert(BpmUserGroupCreateReqVO bean) { + if ( bean == null ) { + return null; + } + + BpmUserGroupDOBuilder bpmUserGroupDO = BpmUserGroupDO.builder(); + + bpmUserGroupDO.name( bean.getName() ); + bpmUserGroupDO.description( bean.getDescription() ); + bpmUserGroupDO.status( bean.getStatus() ); + Set set = bean.getMemberUserIds(); + if ( set != null ) { + bpmUserGroupDO.memberUserIds( new HashSet( set ) ); + } + + return bpmUserGroupDO.build(); + } + + @Override + public BpmUserGroupDO convert(BpmUserGroupUpdateReqVO bean) { + if ( bean == null ) { + return null; + } + + BpmUserGroupDOBuilder bpmUserGroupDO = BpmUserGroupDO.builder(); + + bpmUserGroupDO.id( bean.getId() ); + bpmUserGroupDO.name( bean.getName() ); + bpmUserGroupDO.description( bean.getDescription() ); + bpmUserGroupDO.status( bean.getStatus() ); + Set set = bean.getMemberUserIds(); + if ( set != null ) { + bpmUserGroupDO.memberUserIds( new HashSet( set ) ); + } + + return bpmUserGroupDO.build(); + } + + @Override + public BpmUserGroupRespVO convert(BpmUserGroupDO bean) { + if ( bean == null ) { + return null; + } + + BpmUserGroupRespVO bpmUserGroupRespVO = new BpmUserGroupRespVO(); + + bpmUserGroupRespVO.setName( bean.getName() ); + bpmUserGroupRespVO.setDescription( bean.getDescription() ); + Set set = bean.getMemberUserIds(); + if ( set != null ) { + bpmUserGroupRespVO.setMemberUserIds( new HashSet( set ) ); + } + bpmUserGroupRespVO.setStatus( bean.getStatus() ); + bpmUserGroupRespVO.setId( bean.getId() ); + bpmUserGroupRespVO.setCreateTime( bean.getCreateTime() ); + + return bpmUserGroupRespVO; + } + + @Override + public List convertList(List list) { + if ( list == null ) { + return null; + } + + List list1 = new ArrayList( list.size() ); + for ( BpmUserGroupDO bpmUserGroupDO : list ) { + list1.add( convert( bpmUserGroupDO ) ); + } + + return list1; + } + + @Override + public PageResult convertPage(PageResult page) { + if ( page == null ) { + return null; + } + + PageResult pageResult = new PageResult(); + + pageResult.setList( convertList( page.getList() ) ); + pageResult.setTotal( page.getTotal() ); + + return pageResult; + } + + @Override + public List convertList2(List list) { + if ( list == null ) { + return null; + } + + List list1 = new ArrayList( list.size() ); + for ( BpmUserGroupDO bpmUserGroupDO : list ) { + list1.add( convert( bpmUserGroupDO ) ); + } + + return list1; + } +} diff --git a/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/message/BpmMessageConvertImpl.java b/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/message/BpmMessageConvertImpl.java new file mode 100644 index 00000000..e4cc21a5 --- /dev/null +++ b/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/message/BpmMessageConvertImpl.java @@ -0,0 +1,38 @@ +package com.ruoyi.flowable.convert.message; + +import com.ruoyi.flowable.domain.dto.send.SmsSendSingleToUserReqDTO; +import java.util.HashMap; +import java.util.Map; +import javax.annotation.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2023-07-11T22:37:16+0800", + comments = "version: 1.4.1.Final, compiler: javac, environment: Java 1.8.0_91 (Oracle Corporation)" +) +public class BpmMessageConvertImpl implements BpmMessageConvert { + + @Override + public SmsSendSingleToUserReqDTO convert(Long userId, String templateCode, Map templateParams) { + if ( userId == null && templateCode == null && templateParams == null ) { + return null; + } + + SmsSendSingleToUserReqDTO smsSendSingleToUserReqDTO = new SmsSendSingleToUserReqDTO(); + + if ( userId != null ) { + smsSendSingleToUserReqDTO.setUserId( userId ); + } + if ( templateCode != null ) { + smsSendSingleToUserReqDTO.setTemplateCode( templateCode ); + } + if ( templateParams != null ) { + Map map = templateParams; + if ( map != null ) { + smsSendSingleToUserReqDTO.setTemplateParams( new HashMap( map ) ); + } + } + + return smsSendSingleToUserReqDTO; + } +} diff --git a/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/oa/BpmOALeaveConvertImpl.java b/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/oa/BpmOALeaveConvertImpl.java new file mode 100644 index 00000000..e5b1887d --- /dev/null +++ b/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/oa/BpmOALeaveConvertImpl.java @@ -0,0 +1,86 @@ +package com.ruoyi.flowable.convert.oa; + +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.flowable.domain.entity.oa.BpmOALeaveDO; +import com.ruoyi.flowable.domain.entity.oa.BpmOALeaveDO.BpmOALeaveDOBuilder; +import com.ruoyi.flowable.domain.vo.oa.BpmOALeaveCreateReqVO; +import com.ruoyi.flowable.domain.vo.oa.BpmOALeaveRespVO; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2023-07-11T22:37:16+0800", + comments = "version: 1.4.1.Final, compiler: javac, environment: Java 1.8.0_91 (Oracle Corporation)" +) +public class BpmOALeaveConvertImpl implements BpmOALeaveConvert { + + @Override + public BpmOALeaveDO convert(BpmOALeaveCreateReqVO bean) { + if ( bean == null ) { + return null; + } + + BpmOALeaveDOBuilder bpmOALeaveDO = BpmOALeaveDO.builder(); + + if ( bean.getType() != null ) { + bpmOALeaveDO.type( String.valueOf( bean.getType() ) ); + } + bpmOALeaveDO.reason( bean.getReason() ); + bpmOALeaveDO.startTime( bean.getStartTime() ); + bpmOALeaveDO.endTime( bean.getEndTime() ); + + return bpmOALeaveDO.build(); + } + + @Override + public BpmOALeaveRespVO convert(BpmOALeaveDO bean) { + if ( bean == null ) { + return null; + } + + BpmOALeaveRespVO bpmOALeaveRespVO = new BpmOALeaveRespVO(); + + bpmOALeaveRespVO.setStartTime( bean.getStartTime() ); + bpmOALeaveRespVO.setEndTime( bean.getEndTime() ); + if ( bean.getType() != null ) { + bpmOALeaveRespVO.setType( Integer.parseInt( bean.getType() ) ); + } + bpmOALeaveRespVO.setReason( bean.getReason() ); + bpmOALeaveRespVO.setId( bean.getId() ); + bpmOALeaveRespVO.setResult( bean.getResult() ); + bpmOALeaveRespVO.setCreateTime( bean.getCreateTime() ); + bpmOALeaveRespVO.setProcessInstanceId( bean.getProcessInstanceId() ); + + return bpmOALeaveRespVO; + } + + @Override + public List convertList(List list) { + if ( list == null ) { + return null; + } + + List list1 = new ArrayList( list.size() ); + for ( BpmOALeaveDO bpmOALeaveDO : list ) { + list1.add( convert( bpmOALeaveDO ) ); + } + + return list1; + } + + @Override + public PageResult convertPage(PageResult page) { + if ( page == null ) { + return null; + } + + PageResult pageResult = new PageResult(); + + pageResult.setList( convertList( page.getList() ) ); + pageResult.setTotal( page.getTotal() ); + + return pageResult; + } +} diff --git a/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/task/BpmActivityConvertImpl.java b/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/task/BpmActivityConvertImpl.java new file mode 100644 index 00000000..b440919d --- /dev/null +++ b/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/task/BpmActivityConvertImpl.java @@ -0,0 +1,46 @@ +package com.ruoyi.flowable.convert.task; + +import com.ruoyi.flowable.domain.vo.activity.BpmActivityRespVO; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Generated; +import org.flowable.engine.history.HistoricActivityInstance; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2023-07-11T22:37:16+0800", + comments = "version: 1.4.1.Final, compiler: javac, environment: Java 1.8.0_91 (Oracle Corporation)" +) +public class BpmActivityConvertImpl implements BpmActivityConvert { + + @Override + public List convertList(List list) { + if ( list == null ) { + return null; + } + + List list1 = new ArrayList( list.size() ); + for ( HistoricActivityInstance historicActivityInstance : list ) { + list1.add( convert( historicActivityInstance ) ); + } + + return list1; + } + + @Override + public BpmActivityRespVO convert(HistoricActivityInstance bean) { + if ( bean == null ) { + return null; + } + + BpmActivityRespVO bpmActivityRespVO = new BpmActivityRespVO(); + + bpmActivityRespVO.setKey( bean.getActivityId() ); + bpmActivityRespVO.setType( bean.getActivityType() ); + bpmActivityRespVO.setStartTime( bean.getStartTime() ); + bpmActivityRespVO.setEndTime( bean.getEndTime() ); + bpmActivityRespVO.setTaskId( bean.getTaskId() ); + + return bpmActivityRespVO; + } +} diff --git a/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/task/BpmProcessInstanceConvertImpl.java b/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/task/BpmProcessInstanceConvertImpl.java new file mode 100644 index 00000000..1433b707 --- /dev/null +++ b/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/task/BpmProcessInstanceConvertImpl.java @@ -0,0 +1,188 @@ +package com.ruoyi.flowable.convert.task; + +import com.ruoyi.flowable.domain.dto.user.AdminUserRespDTO; +import com.ruoyi.flowable.domain.entity.definition.BpmProcessDefinitionExtDO; +import com.ruoyi.flowable.domain.entity.task.BpmProcessInstanceExtDO; +import com.ruoyi.flowable.domain.vo.instance.BpmProcessInstancePageItemRespVO; +import com.ruoyi.flowable.domain.vo.instance.BpmProcessInstanceRespVO; +import com.ruoyi.flowable.domain.vo.instance.BpmProcessInstanceRespVO.User; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.annotation.Generated; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.task.api.Task; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2023-07-11T22:37:16+0800", + comments = "version: 1.4.1.Final, compiler: javac, environment: Java 1.8.0_91 (Oracle Corporation)" +) +public class BpmProcessInstanceConvertImpl implements BpmProcessInstanceConvert { + + @Override + public List convertList(List list) { + if ( list == null ) { + return null; + } + + List list1 = new ArrayList( list.size() ); + for ( BpmProcessInstanceExtDO bpmProcessInstanceExtDO : list ) { + list1.add( convert( bpmProcessInstanceExtDO ) ); + } + + return list1; + } + + @Override + public BpmProcessInstancePageItemRespVO convert(BpmProcessInstanceExtDO bean) { + if ( bean == null ) { + return null; + } + + BpmProcessInstancePageItemRespVO bpmProcessInstancePageItemRespVO = new BpmProcessInstancePageItemRespVO(); + + bpmProcessInstancePageItemRespVO.setId( bean.getProcessInstanceId() ); + bpmProcessInstancePageItemRespVO.setName( bean.getName() ); + bpmProcessInstancePageItemRespVO.setProcessDefinitionId( bean.getProcessDefinitionId() ); + bpmProcessInstancePageItemRespVO.setCategory( bean.getCategory() ); + bpmProcessInstancePageItemRespVO.setStatus( bean.getStatus() ); + bpmProcessInstancePageItemRespVO.setResult( bean.getResult() ); + bpmProcessInstancePageItemRespVO.setCreateTime( bean.getCreateTime() ); + bpmProcessInstancePageItemRespVO.setEndTime( bean.getEndTime() ); + + return bpmProcessInstancePageItemRespVO; + } + + @Override + public List convertList2(List tasks) { + if ( tasks == null ) { + return null; + } + + List list = new ArrayList( tasks.size() ); + for ( Task task : tasks ) { + list.add( taskToTask( task ) ); + } + + return list; + } + + @Override + public BpmProcessInstanceRespVO convert2(HistoricProcessInstance bean) { + if ( bean == null ) { + return null; + } + + BpmProcessInstanceRespVO bpmProcessInstanceRespVO = new BpmProcessInstanceRespVO(); + + bpmProcessInstanceRespVO.setId( bean.getId() ); + bpmProcessInstanceRespVO.setName( bean.getName() ); + bpmProcessInstanceRespVO.setEndTime( bean.getEndTime() ); + bpmProcessInstanceRespVO.setBusinessKey( bean.getBusinessKey() ); + + return bpmProcessInstanceRespVO; + } + + @Override + public void copyTo(BpmProcessInstanceExtDO from, BpmProcessInstanceRespVO to) { + if ( from == null ) { + return; + } + + to.setName( from.getName() ); + to.setCategory( from.getCategory() ); + to.setStatus( from.getStatus() ); + to.setResult( from.getResult() ); + to.setCreateTime( from.getCreateTime() ); + to.setEndTime( from.getEndTime() ); + if ( to.getFormVariables() != null ) { + Map map = from.getFormVariables(); + if ( map != null ) { + to.getFormVariables().clear(); + to.getFormVariables().putAll( map ); + } + else { + to.setFormVariables( null ); + } + } + else { + Map map = from.getFormVariables(); + if ( map != null ) { + to.setFormVariables( new HashMap( map ) ); + } + } + } + + @Override + public com.ruoyi.flowable.domain.vo.instance.BpmProcessInstanceRespVO.ProcessDefinition convert2(ProcessDefinition bean) { + if ( bean == null ) { + return null; + } + + com.ruoyi.flowable.domain.vo.instance.BpmProcessInstanceRespVO.ProcessDefinition processDefinition = new com.ruoyi.flowable.domain.vo.instance.BpmProcessInstanceRespVO.ProcessDefinition(); + + processDefinition.setId( bean.getId() ); + + return processDefinition; + } + + @Override + public void copyTo(BpmProcessDefinitionExtDO from, com.ruoyi.flowable.domain.vo.instance.BpmProcessInstanceRespVO.ProcessDefinition to) { + if ( from == null ) { + return; + } + + to.setFormType( from.getFormType() ); + to.setFormId( from.getFormId() ); + to.setFormConf( from.getFormConf() ); + if ( to.getFormFields() != null ) { + List list = from.getFormFields(); + if ( list != null ) { + to.getFormFields().clear(); + to.getFormFields().addAll( list ); + } + else { + to.setFormFields( null ); + } + } + else { + List list = from.getFormFields(); + if ( list != null ) { + to.setFormFields( new ArrayList( list ) ); + } + } + to.setFormCustomCreatePath( from.getFormCustomCreatePath() ); + to.setFormCustomViewPath( from.getFormCustomViewPath() ); + } + + @Override + public User convert2(AdminUserRespDTO bean) { + if ( bean == null ) { + return null; + } + + User user = new User(); + + user.setId( bean.getId() ); + user.setNickname( bean.getNickname() ); + user.setDeptId( bean.getDeptId() ); + + return user; + } + + protected com.ruoyi.flowable.domain.vo.instance.BpmProcessInstancePageItemRespVO.Task taskToTask(Task task) { + if ( task == null ) { + return null; + } + + com.ruoyi.flowable.domain.vo.instance.BpmProcessInstancePageItemRespVO.Task task1 = new com.ruoyi.flowable.domain.vo.instance.BpmProcessInstancePageItemRespVO.Task(); + + task1.setId( task.getId() ); + task1.setName( task.getName() ); + + return task1; + } +} diff --git a/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/task/BpmTaskConvertImpl.java b/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/task/BpmTaskConvertImpl.java new file mode 100644 index 00000000..f661e30e --- /dev/null +++ b/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/task/BpmTaskConvertImpl.java @@ -0,0 +1,156 @@ +package com.ruoyi.flowable.convert.task; + +import com.ruoyi.flowable.domain.dto.user.AdminUserRespDTO; +import com.ruoyi.flowable.domain.entity.task.BpmTaskExtDO; +import com.ruoyi.flowable.domain.vo.task.BpmTaskDonePageItemRespVO; +import com.ruoyi.flowable.domain.vo.task.BpmTaskRespVO; +import com.ruoyi.flowable.domain.vo.task.BpmTaskRespVO.User; +import com.ruoyi.flowable.domain.vo.task.BpmTaskTodoPageItemRespVO; +import com.ruoyi.flowable.domain.vo.task.BpmTaskTodoPageItemRespVO.ProcessInstance; +import javax.annotation.Generated; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.task.api.Task; +import org.flowable.task.api.history.HistoricTaskInstance; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2023-07-11T22:37:16+0800", + comments = "version: 1.4.1.Final, compiler: javac, environment: Java 1.8.0_91 (Oracle Corporation)" +) +public class BpmTaskConvertImpl implements BpmTaskConvert { + + @Override + public BpmTaskTodoPageItemRespVO convert1(Task bean) { + if ( bean == null ) { + return null; + } + + BpmTaskTodoPageItemRespVO bpmTaskTodoPageItemRespVO = new BpmTaskTodoPageItemRespVO(); + + bpmTaskTodoPageItemRespVO.setSuspensionState( convertSuspendedToSuspensionState( bean.isSuspended() ) ); + bpmTaskTodoPageItemRespVO.setId( bean.getId() ); + bpmTaskTodoPageItemRespVO.setName( bean.getName() ); + bpmTaskTodoPageItemRespVO.setClaimTime( bean.getClaimTime() ); + bpmTaskTodoPageItemRespVO.setCreateTime( bean.getCreateTime() ); + bpmTaskTodoPageItemRespVO.setTaskDefinitionKey( bean.getTaskDefinitionKey() ); + + return bpmTaskTodoPageItemRespVO; + } + + @Override + public BpmTaskDonePageItemRespVO convert2(HistoricTaskInstance bean) { + if ( bean == null ) { + return null; + } + + BpmTaskDonePageItemRespVO bpmTaskDonePageItemRespVO = new BpmTaskDonePageItemRespVO(); + + bpmTaskDonePageItemRespVO.setId( bean.getId() ); + bpmTaskDonePageItemRespVO.setName( bean.getName() ); + bpmTaskDonePageItemRespVO.setClaimTime( bean.getClaimTime() ); + bpmTaskDonePageItemRespVO.setCreateTime( bean.getCreateTime() ); + bpmTaskDonePageItemRespVO.setTaskDefinitionKey( bean.getTaskDefinitionKey() ); + bpmTaskDonePageItemRespVO.setEndTime( bean.getEndTime() ); + bpmTaskDonePageItemRespVO.setDurationInMillis( bean.getDurationInMillis() ); + + return bpmTaskDonePageItemRespVO; + } + + @Override + public ProcessInstance convert(org.flowable.engine.runtime.ProcessInstance processInstance, AdminUserRespDTO startUser) { + if ( processInstance == null && startUser == null ) { + return null; + } + + ProcessInstance processInstance1 = new ProcessInstance(); + + if ( processInstance != null ) { + processInstance1.setId( processInstance.getId() ); + processInstance1.setName( processInstance.getName() ); + if ( processInstance.getStartUserId() != null ) { + processInstance1.setStartUserId( Long.parseLong( processInstance.getStartUserId() ) ); + } + processInstance1.setProcessDefinitionId( processInstance.getProcessDefinitionId() ); + processInstance1.setBusinessKey( processInstance.getBusinessKey() ); + processInstance1.setProcessDefinitionKey( processInstance.getProcessDefinitionKey() ); + } + if ( startUser != null ) { + processInstance1.setStartUserNickname( startUser.getNickname() ); + } + + return processInstance1; + } + + @Override + public BpmTaskRespVO convert3(HistoricTaskInstance bean) { + if ( bean == null ) { + return null; + } + + BpmTaskRespVO bpmTaskRespVO = new BpmTaskRespVO(); + + bpmTaskRespVO.setDefinitionKey( bean.getTaskDefinitionKey() ); + bpmTaskRespVO.setId( bean.getId() ); + bpmTaskRespVO.setName( bean.getName() ); + bpmTaskRespVO.setClaimTime( bean.getClaimTime() ); + bpmTaskRespVO.setCreateTime( bean.getCreateTime() ); + bpmTaskRespVO.setTaskDefinitionKey( bean.getTaskDefinitionKey() ); + bpmTaskRespVO.setEndTime( bean.getEndTime() ); + bpmTaskRespVO.setDurationInMillis( bean.getDurationInMillis() ); + + return bpmTaskRespVO; + } + + @Override + public User convert3(AdminUserRespDTO bean) { + if ( bean == null ) { + return null; + } + + User user = new User(); + + user.setId( bean.getId() ); + user.setNickname( bean.getNickname() ); + user.setDeptId( bean.getDeptId() ); + + return user; + } + + @Override + public void copyTo(BpmTaskExtDO from, BpmTaskDonePageItemRespVO to) { + if ( from == null ) { + return; + } + + to.setName( from.getName() ); + to.setCreateTime( from.getCreateTime() ); + to.setEndTime( from.getEndTime() ); + to.setResult( from.getResult() ); + to.setReason( from.getReason() ); + } + + @Override + public ProcessInstance convert(HistoricProcessInstance processInstance, AdminUserRespDTO startUser) { + if ( processInstance == null && startUser == null ) { + return null; + } + + ProcessInstance processInstance1 = new ProcessInstance(); + + if ( processInstance != null ) { + processInstance1.setId( processInstance.getId() ); + processInstance1.setName( processInstance.getName() ); + if ( processInstance.getStartUserId() != null ) { + processInstance1.setStartUserId( Long.parseLong( processInstance.getStartUserId() ) ); + } + processInstance1.setProcessDefinitionId( processInstance.getProcessDefinitionId() ); + processInstance1.setBusinessKey( processInstance.getBusinessKey() ); + processInstance1.setProcessDefinitionKey( processInstance.getProcessDefinitionKey() ); + } + if ( startUser != null ) { + processInstance1.setStartUserNickname( startUser.getNickname() ); + } + + return processInstance1; + } +} diff --git a/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/user/DeptConvertImpl.java b/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/user/DeptConvertImpl.java new file mode 100644 index 00000000..032ccc3b --- /dev/null +++ b/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/user/DeptConvertImpl.java @@ -0,0 +1,47 @@ +package com.ruoyi.flowable.convert.user; + +import com.ruoyi.common.core.domain.entity.SysDept; +import com.ruoyi.flowable.domain.dto.user.DeptRespDTO; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2023-07-11T22:37:16+0800", + comments = "version: 1.4.1.Final, compiler: javac, environment: Java 1.8.0_91 (Oracle Corporation)" +) +public class DeptConvertImpl implements DeptConvert { + + @Override + public DeptRespDTO convert(SysDept bean) { + if ( bean == null ) { + return null; + } + + DeptRespDTO deptRespDTO = new DeptRespDTO(); + + deptRespDTO.setId( bean.getDeptId() ); + deptRespDTO.setName( bean.getDeptName() ); + deptRespDTO.setParentId( bean.getParentId() ); + if ( bean.getStatus() != null ) { + deptRespDTO.setStatus( Integer.parseInt( bean.getStatus() ) ); + } + + return deptRespDTO; + } + + @Override + public List convertList(List list) { + if ( list == null ) { + return null; + } + + List list1 = new ArrayList( list.size() ); + for ( SysDept sysDept : list ) { + list1.add( convert( sysDept ) ); + } + + return list1; + } +} diff --git a/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/user/UserConvertImpl.java b/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/user/UserConvertImpl.java new file mode 100644 index 00000000..23c8e343 --- /dev/null +++ b/ruoyi-flowable/target/generated-sources/annotations/com/ruoyi/flowable/convert/user/UserConvertImpl.java @@ -0,0 +1,64 @@ +package com.ruoyi.flowable.convert.user; + +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.flowable.domain.dto.user.AdminUserRespDTO; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import javax.annotation.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2023-07-11T22:37:16+0800", + comments = "version: 1.4.1.Final, compiler: javac, environment: Java 1.8.0_91 (Oracle Corporation)" +) +public class UserConvertImpl implements UserConvert { + + @Override + public AdminUserRespDTO convert(SysUser bean) { + if ( bean == null ) { + return null; + } + + AdminUserRespDTO adminUserRespDTO = new AdminUserRespDTO(); + + adminUserRespDTO.setId( bean.getUserId() ); + adminUserRespDTO.setNickname( bean.getNickName() ); + adminUserRespDTO.setMobile( bean.getPhonenumber() ); + if ( bean.getStatus() != null ) { + adminUserRespDTO.setStatus( Integer.parseInt( bean.getStatus() ) ); + } + adminUserRespDTO.setDeptId( bean.getDeptId() ); + adminUserRespDTO.setPostIds( longArrayToLongSet( bean.getPostIds() ) ); + + return adminUserRespDTO; + } + + @Override + public List convertList(List users) { + if ( users == null ) { + return null; + } + + List list = new ArrayList( users.size() ); + for ( SysUser sysUser : users ) { + list.add( convert( sysUser ) ); + } + + return list; + } + + protected Set longArrayToLongSet(Long[] longArray) { + if ( longArray == null ) { + return null; + } + + Set set = new HashSet( Math.max( (int) ( longArray.length / .75f ) + 1, 16 ) ); + for ( Long long1 : longArray ) { + set.add( long1 ); + } + + return set; + } +} diff --git a/ruoyi-framework/pom.xml b/ruoyi-framework/pom.xml new file mode 100644 index 00000000..a0c7ed4d --- /dev/null +++ b/ruoyi-framework/pom.xml @@ -0,0 +1,82 @@ + + + + ruoyi + com.ruoyi + 3.8.4 + + 4.0.0 + + ruoyi-framework + + + framework框架核心 + + + + + + + org.springframework.boot + spring-boot-starter-web + + + + + org.springframework.boot + spring-boot-starter-aop + + + + + com.alibaba + druid-spring-boot-starter + + + + + com.github.penggle + kaptcha + + + javax.servlet-api + javax.servlet + + + + + + + com.github.oshi + oshi-core + + + + + com.ruoyi + ruoyi-system + + + + + org.springframework.boot + spring-boot-starter-websocket + + + + + org.projectlombok + lombok + true + + + + + + + + + + diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java new file mode 100644 index 00000000..760dd130 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java @@ -0,0 +1,167 @@ +package com.ruoyi.framework.aspectj; + +import java.util.ArrayList; +import java.util.List; + +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.framework.security.context.PermissionContextHolder; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.stereotype.Component; +import com.ruoyi.common.annotation.DataScope; +import com.ruoyi.common.core.domain.BaseEntity; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.SecurityUtils; + +/** + * 数据过滤处理 + * + * @author ruoyi + */ +@Aspect +@Component +public class DataScopeAspect +{ + /** + * 全部数据权限 + */ + public static final String DATA_SCOPE_ALL = "1"; + + /** + * 自定数据权限 + */ + public static final String DATA_SCOPE_CUSTOM = "2"; + + /** + * 部门数据权限 + */ + public static final String DATA_SCOPE_DEPT = "3"; + + /** + * 部门及以下数据权限 + */ + public static final String DATA_SCOPE_DEPT_AND_CHILD = "4"; + + /** + * 仅本人数据权限 + */ + public static final String DATA_SCOPE_SELF = "5"; + + /** + * 数据权限过滤关键字 + */ + public static final String DATA_SCOPE = "dataScope"; + + @Before("@annotation(controllerDataScope)") + public void doBefore(JoinPoint point, DataScope controllerDataScope) throws Throwable + { + clearDataScope(point); + handleDataScope(point, controllerDataScope); + } + + protected void handleDataScope(final JoinPoint joinPoint, DataScope controllerDataScope) + { + // 获取当前的用户 + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNotNull(loginUser)) + { + String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(), PermissionContextHolder.getContext()); + SysUser currentUser = loginUser.getUser(); + // 如果是超级管理员,则不过滤数据 + if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin()) + { + dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(), + controllerDataScope.userAlias(), permission); + } + } + } + + /** + * 数据范围过滤 + * + * @param joinPoint 切点 + * @param user 用户 + * @param deptAlias 部门别名 + * @param userAlias 用户别名 + */ + public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias, String permission) + { + StringBuilder sqlString = new StringBuilder(); + List conditions = new ArrayList(); + + for (SysRole role : user.getRoles()) + { + String dataScope = role.getDataScope(); + if (!DATA_SCOPE_CUSTOM.equals(dataScope) && conditions.contains(dataScope)) + { + continue; + } + if (StringUtils.isNotEmpty(permission) && StringUtils.isNotEmpty(role.getPermissions()) + && !StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission))) + { + continue; + } + if (DATA_SCOPE_ALL.equals(dataScope)) + { + sqlString = new StringBuilder(); + break; + } + else if (DATA_SCOPE_CUSTOM.equals(dataScope)) + { + sqlString.append(StringUtils.format( + " OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias, + role.getRoleId())); + } + else if (DATA_SCOPE_DEPT.equals(dataScope)) + { + sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId())); + } + else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) + { + sqlString.append(StringUtils.format( + " OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )", + deptAlias, user.getDeptId(), user.getDeptId())); + } + else if (DATA_SCOPE_SELF.equals(dataScope)) + { + if (StringUtils.isNotBlank(userAlias)) + { + sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId())); + } + else + { + // 数据权限为仅本人且没有userAlias别名不查询任何数据 + sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias)); + } + } + conditions.add(dataScope); + } + + if (StringUtils.isNotBlank(sqlString.toString())) + { + Object params = joinPoint.getArgs()[0]; + if (StringUtils.isNotNull(params) && params instanceof BaseEntity) + { + BaseEntity baseEntity = (BaseEntity) params; + baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")"); + } + } + } + + /** + * 拼接权限sql前先清空params.dataScope参数防止注入 + */ + private void clearDataScope(final JoinPoint joinPoint) + { + Object params = joinPoint.getArgs()[0]; + if (StringUtils.isNotNull(params) && params instanceof BaseEntity) + { + BaseEntity baseEntity = (BaseEntity) params; + baseEntity.getParams().put(DATA_SCOPE, ""); + } + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java new file mode 100644 index 00000000..8c2c9f43 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java @@ -0,0 +1,72 @@ +package com.ruoyi.framework.aspectj; + +import java.util.Objects; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import com.ruoyi.common.annotation.DataSource; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.datasource.DynamicDataSourceContextHolder; + +/** + * 多数据源处理 + * + * @author ruoyi + */ +@Aspect +@Order(1) +@Component +public class DataSourceAspect +{ + protected Logger logger = LoggerFactory.getLogger(getClass()); + + @Pointcut("@annotation(com.ruoyi.common.annotation.DataSource)" + + "|| @within(com.ruoyi.common.annotation.DataSource)") + public void dsPointCut() + { + + } + + @Around("dsPointCut()") + public Object around(ProceedingJoinPoint point) throws Throwable + { + DataSource dataSource = getDataSource(point); + + if (StringUtils.isNotNull(dataSource)) + { + DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name()); + } + + try + { + return point.proceed(); + } + finally + { + // 销毁数据源 在执行方法之后 + DynamicDataSourceContextHolder.clearDataSourceType(); + } + } + + /** + * 获取需要切换的数据源 + */ + public DataSource getDataSource(ProceedingJoinPoint point) + { + MethodSignature signature = (MethodSignature) point.getSignature(); + DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class); + if (Objects.nonNull(dataSource)) + { + return dataSource; + } + + return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java new file mode 100644 index 00000000..fef51fd3 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java @@ -0,0 +1,200 @@ +package com.ruoyi.framework.aspectj; + +import com.alibaba.fastjson2.JSON; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.enums.BusinessStatus; +import com.ruoyi.common.enums.HttpMethod; +import com.ruoyi.common.filter.PropertyPreExcludeFilter; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.system.domain.SysOperLog; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Aspect; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.validation.BindingResult; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.HandlerMapping; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.Collection; +import java.util.Map; + +/** + * 操作日志记录处理 + * + * @author ruoyi + */ +@Aspect +@Component +public class LogAspect { + private static final Logger log = LoggerFactory.getLogger(LogAspect.class); + + /** + * 排除敏感属性字段 + */ + public static final String[] EXCLUDE_PROPERTIES = {"password", "oldPassword", "newPassword", "confirmPassword"}; + + /** + * 处理完请求后执行 + * + * @param joinPoint 切点 + */ + @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult") + public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) { + handleLog(joinPoint, controllerLog, null, jsonResult); + } + + /** + * 拦截异常操作 + * + * @param joinPoint 切点 + * @param e 异常 + */ + @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e") + public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) { + handleLog(joinPoint, controllerLog, e, null); + } + + protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) { + try { + // 获取当前的用户 + LoginUser loginUser = SecurityUtils.getLoginUser(); + + // *========数据库日志=========*// + SysOperLog operLog = new SysOperLog(); + operLog.setStatus(BusinessStatus.SUCCESS.ordinal()); + // 请求的地址 + String ip = IpUtils.getIpAddr(ServletUtils.getRequest()); + operLog.setOperIp(ip); + operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255)); + if (loginUser != null) { + operLog.setOperName(loginUser.getUsername()); + } + + if (e != null) { + operLog.setStatus(BusinessStatus.FAIL.ordinal()); + operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000)); + } + // 设置方法名称 + String className = joinPoint.getTarget().getClass().getName(); + String methodName = joinPoint.getSignature().getName(); + operLog.setMethod(className + "." + methodName + "()"); + // 设置请求方式 + operLog.setRequestMethod(ServletUtils.getRequest().getMethod()); + // 处理设置注解上的参数 + getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult); + // 保存数据库 + AsyncManager.me().execute(AsyncFactory.recordOper(operLog)); + } catch (Exception exp) { + // 记录本地异常日志 + log.error("==前置通知异常=="); + log.error("异常信息:{}", exp.getMessage()); + exp.printStackTrace(); + } + } + + /** + * 获取注解中对方法的描述信息 用于Controller层注解 + * + * @param log 日志 + * @param operLog 操作日志 + * @throws Exception + */ + public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog, Object jsonResult) throws Exception { + // 设置action动作 + operLog.setBusinessType(log.businessType().ordinal()); + // 设置标题 + operLog.setTitle(log.title()); + // 设置操作人类别 + operLog.setOperatorType(log.operatorType().ordinal()); + // 是否需要保存request,参数和值 + if (log.isSaveRequestData()) { + // 获取参数的信息,传入到数据库中。 + setRequestValue(joinPoint, operLog); + } + // 是否需要保存response,参数和值 + if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult)) { + operLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, 2000)); + } + } + + /** + * 获取请求的参数,放到log中 + * + * @param operLog 操作日志 + * @throws Exception 异常 + */ + private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog) throws Exception { + String requestMethod = operLog.getRequestMethod(); + if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) { + String params = argsArrayToString(joinPoint.getArgs()); + operLog.setOperParam(StringUtils.substring(params, 0, 2000)); + } else { + Map paramsMap = (Map) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE); + operLog.setOperParam(StringUtils.substring(paramsMap.toString(), 0, 2000)); + } + } + + /** + * 参数拼装 + */ + private String argsArrayToString(Object[] paramsArray) { + String params = ""; + if (paramsArray != null && paramsArray.length > 0) { + for (Object o : paramsArray) { + if (StringUtils.isNotNull(o) && !isFilterObject(o)) { + try { + String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter()); + params += jsonObj.toString() + " "; + } catch (Exception e) { + } + } + } + } + return params.trim(); + } + + /** + * 忽略敏感属性 + */ + public PropertyPreExcludeFilter excludePropertyPreFilter() { + return new PropertyPreExcludeFilter().addExcludes(EXCLUDE_PROPERTIES); + } + + /** + * 判断是否需要过滤的对象。 + * + * @param o 对象信息。 + * @return 如果是需要过滤的对象,则返回true;否则返回false。 + */ + @SuppressWarnings("rawtypes") + public boolean isFilterObject(final Object o) { + Class clazz = o.getClass(); + if (clazz.isArray()) { + return clazz.getComponentType().isAssignableFrom(MultipartFile.class); + } else if (Collection.class.isAssignableFrom(clazz)) { + Collection collection = (Collection) o; + for (Object value : collection) { + return value instanceof MultipartFile; + } + } else if (Map.class.isAssignableFrom(clazz)) { + Map map = (Map) o; + for (Object value : map.entrySet()) { + Map.Entry entry = (Map.Entry) value; + return entry.getValue() instanceof MultipartFile; + } + } + return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse + || o instanceof BindingResult; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java new file mode 100644 index 00000000..92ff9c86 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java @@ -0,0 +1,80 @@ +//package com.ruoyi.framework.aspectj; +// +//import com.ruoyi.common.annotation.RateLimiter; +//import com.ruoyi.common.enums.LimitType; +//import com.ruoyi.common.exception.ServiceException; +//import com.ruoyi.common.utils.ServletUtils; +//import com.ruoyi.common.utils.StringUtils; +//import com.ruoyi.common.utils.ip.IpUtils; +//import org.aspectj.lang.JoinPoint; +//import org.aspectj.lang.annotation.Aspect; +//import org.aspectj.lang.annotation.Before; +//import org.aspectj.lang.reflect.MethodSignature; +//import org.slf4j.Logger; +//import org.slf4j.LoggerFactory; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.data.redis.core.RedisTemplate; +//import org.springframework.data.redis.core.script.RedisScript; +//import org.springframework.stereotype.Component; +// +//import java.lang.reflect.Method; +//import java.util.Collections; +//import java.util.List; +// +///** +// * 限流处理 +// * +// * @author ruoyi +// */ +//@Aspect +//@Component +//public class RateLimiterAspect { +// private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class); +// +// private RedisTemplate redisTemplate; +// +// private RedisScript limitScript; +// +// @Autowired +// public void setRedisTemplate1(RedisTemplate redisTemplate) { +// this.redisTemplate = redisTemplate; +// } +// +// @Autowired +// public void setLimitScript(RedisScript limitScript) { +// this.limitScript = limitScript; +// } +// +// @Before("@annotation(rateLimiter)") +// public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable { +// String key = rateLimiter.key(); +// int time = rateLimiter.time(); +// int count = rateLimiter.count(); +// +// String combineKey = getCombineKey(rateLimiter, point); +// List keys = Collections.singletonList(combineKey); +// try { +// Long number = redisTemplate.execute(limitScript, keys, count, time); +// if (StringUtils.isNull(number) || number.intValue() > count) { +// throw new ServiceException("访问过于频繁,请稍候再试"); +// } +// log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number.intValue(), combineKey); +// } catch (ServiceException e) { +// throw e; +// } catch (Exception e) { +// throw new RuntimeException("服务器限流异常,请稍候再试"); +// } +// } +// +// public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) { +// StringBuffer stringBuffer = new StringBuffer(rateLimiter.key()); +// if (rateLimiter.limitType() == LimitType.IP) { +// stringBuffer.append(IpUtils.getIpAddr(ServletUtils.getRequest())).append("-"); +// } +// MethodSignature signature = (MethodSignature) point.getSignature(); +// Method method = signature.getMethod(); +// Class targetClass = method.getDeclaringClass(); +// stringBuffer.append(targetClass.getName()).append("-").append(method.getName()); +// return stringBuffer.toString(); +// } +//} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java new file mode 100644 index 00000000..1d4dc1f7 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java @@ -0,0 +1,30 @@ +package com.ruoyi.framework.config; + +import java.util.TimeZone; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; + +/** + * 程序注解配置 + * + * @author ruoyi + */ +@Configuration +// 表示通过aop框架暴露该代理对象,AopContext能够访问 +@EnableAspectJAutoProxy(exposeProxy = true) +// 指定要扫描的Mapper类的包的路径 +@MapperScan("com.ruoyi.**.mapper") +public class ApplicationConfig +{ + /** + * 时区配置 + */ + @Bean + public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() + { + return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault()); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/CaptchaConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/CaptchaConfig.java new file mode 100644 index 00000000..43e78aeb --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/CaptchaConfig.java @@ -0,0 +1,83 @@ +package com.ruoyi.framework.config; + +import java.util.Properties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import com.google.code.kaptcha.impl.DefaultKaptcha; +import com.google.code.kaptcha.util.Config; +import static com.google.code.kaptcha.Constants.*; + +/** + * 验证码配置 + * + * @author ruoyi + */ +@Configuration +public class CaptchaConfig +{ + @Bean(name = "captchaProducer") + public DefaultKaptcha getKaptchaBean() + { + DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); + Properties properties = new Properties(); + // 是否有边框 默认为true 我们可以自己设置yes,no + properties.setProperty(KAPTCHA_BORDER, "yes"); + // 验证码文本字符颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black"); + // 验证码图片宽度 默认为200 + properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160"); + // 验证码图片高度 默认为50 + properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60"); + // 验证码文本字符大小 默认为40 + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38"); + // KAPTCHA_SESSION_KEY + properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode"); + // 验证码文本字符长度 默认为5 + properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4"); + // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize) + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier"); + // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy + properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy"); + Config config = new Config(properties); + defaultKaptcha.setConfig(config); + return defaultKaptcha; + } + + @Bean(name = "captchaProducerMath") + public DefaultKaptcha getKaptchaBeanMath() + { + DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); + Properties properties = new Properties(); + // 是否有边框 默认为true 我们可以自己设置yes,no + properties.setProperty(KAPTCHA_BORDER, "yes"); + // 边框颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90"); + // 验证码文本字符颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue"); + // 验证码图片宽度 默认为200 + properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160"); + // 验证码图片高度 默认为50 + properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60"); + // 验证码文本字符大小 默认为40 + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35"); + // KAPTCHA_SESSION_KEY + properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath"); + // 验证码文本生成器 + properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.ruoyi.framework.config.KaptchaTextCreator"); + // 验证码文本字符间距 默认为2 + properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3"); + // 验证码文本字符长度 默认为5 + properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6"); + // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize) + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier"); + // 验证码噪点颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_NOISE_COLOR, "white"); + // 干扰实现类 + properties.setProperty(KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise"); + // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy + properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy"); + Config config = new Config(properties); + defaultKaptcha.setConfig(config); + return defaultKaptcha; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java new file mode 100644 index 00000000..f6abac13 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java @@ -0,0 +1,126 @@ +package com.ruoyi.framework.config; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.sql.DataSource; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; +import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties; +import com.alibaba.druid.util.Utils; +import com.ruoyi.common.enums.DataSourceType; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.framework.config.properties.DruidProperties; +import com.ruoyi.framework.datasource.DynamicDataSource; + +/** + * druid 配置多数据源 + * + * @author ruoyi + */ +@Configuration +public class DruidConfig +{ + @Bean + @ConfigurationProperties("spring.datasource.druid.master") + public DataSource masterDataSource(DruidProperties druidProperties) + { + DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); + return druidProperties.dataSource(dataSource); + } + + @Bean + @ConfigurationProperties("spring.datasource.druid.slave") + @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true") + public DataSource slaveDataSource(DruidProperties druidProperties) + { + DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); + return druidProperties.dataSource(dataSource); + } + + @Bean(name = "dynamicDataSource") + @Primary + public DynamicDataSource dataSource(DataSource masterDataSource) + { + Map targetDataSources = new HashMap<>(); + targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource); + setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource"); + return new DynamicDataSource(masterDataSource, targetDataSources); + } + + /** + * 设置数据源 + * + * @param targetDataSources 备选数据源集合 + * @param sourceName 数据源名称 + * @param beanName bean名称 + */ + public void setDataSource(Map targetDataSources, String sourceName, String beanName) + { + try + { + DataSource dataSource = SpringUtils.getBean(beanName); + targetDataSources.put(sourceName, dataSource); + } + catch (Exception e) + { + } + } + + /** + * 去除监控页面底部的广告 + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + @ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true") + public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties) + { + // 获取web监控页面的参数 + DruidStatProperties.StatViewServlet config = properties.getStatViewServlet(); + // 提取common.js的配置路径 + String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*"; + String commonJsPattern = pattern.replaceAll("\\*", "js/common.js"); + final String filePath = "support/http/resources/js/common.js"; + // 创建filter进行过滤 + Filter filter = new Filter() + { + @Override + public void init(javax.servlet.FilterConfig filterConfig) throws ServletException + { + } + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + chain.doFilter(request, response); + // 重置缓冲区,响应头不会被重置 + response.resetBuffer(); + // 获取common.js + String text = Utils.readFromResource(filePath); + // 正则替换banner, 除去底部的广告信息 + text = text.replaceAll("
", ""); + text = text.replaceAll("powered.*?shrek.wang", ""); + response.getWriter().write(text); + } + @Override + public void destroy() + { + } + }; + FilterRegistrationBean registrationBean = new FilterRegistrationBean(); + registrationBean.setFilter(filter); + registrationBean.addUrlPatterns(commonJsPattern); + return registrationBean; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java new file mode 100644 index 00000000..20bd8706 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java @@ -0,0 +1,48 @@ +//package com.ruoyi.framework.config; +// +//import java.nio.charset.Charset; +//import org.springframework.data.redis.serializer.RedisSerializer; +//import org.springframework.data.redis.serializer.SerializationException; +//import com.alibaba.fastjson2.JSON; +//import com.alibaba.fastjson2.JSONReader; +//import com.alibaba.fastjson2.JSONWriter; +// +///** +// * Redis使用FastJson序列化 +// * +// * @author ruoyi +// */ +//public class FastJson2JsonRedisSerializer implements RedisSerializer +//{ +// public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); +// +// private Class clazz; +// +// public FastJson2JsonRedisSerializer(Class clazz) +// { +// super(); +// this.clazz = clazz; +// } +// +// @Override +// public byte[] serialize(T t) throws SerializationException +// { +// if (t == null) +// { +// return new byte[0]; +// } +// return JSON.toJSONString(t, JSONWriter.Feature.WriteClassName).getBytes(DEFAULT_CHARSET); +// } +// +// @Override +// public T deserialize(byte[] bytes) throws SerializationException +// { +// if (bytes == null || bytes.length <= 0) +// { +// return null; +// } +// String str = new String(bytes, DEFAULT_CHARSET); +// +// return JSON.parseObject(str, clazz, JSONReader.Feature.SupportAutoType); +// } +//} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java new file mode 100644 index 00000000..c5e6b15b --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java @@ -0,0 +1,58 @@ +package com.ruoyi.framework.config; + +import java.util.HashMap; +import java.util.Map; +import javax.servlet.DispatcherType; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import com.ruoyi.common.filter.RepeatableFilter; +import com.ruoyi.common.filter.XssFilter; +import com.ruoyi.common.utils.StringUtils; + +/** + * Filter配置 + * + * @author ruoyi + */ +@Configuration +public class FilterConfig +{ + @Value("${xss.excludes}") + private String excludes; + + @Value("${xss.urlPatterns}") + private String urlPatterns; + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + @ConditionalOnProperty(value = "xss.enabled", havingValue = "true") + public FilterRegistrationBean xssFilterRegistration() + { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setDispatcherTypes(DispatcherType.REQUEST); + registration.setFilter(new XssFilter()); + registration.addUrlPatterns(StringUtils.split(urlPatterns, ",")); + registration.setName("xssFilter"); + registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE); + Map initParameters = new HashMap(); + initParameters.put("excludes", excludes); + registration.setInitParameters(initParameters); + return registration; + } + +// @SuppressWarnings({ "rawtypes", "unchecked" }) +// @Bean +// public FilterRegistrationBean someFilterRegistration() +// { +// FilterRegistrationBean registration = new FilterRegistrationBean(); +// registration.setFilter(new RepeatableFilter()); +// registration.addUrlPatterns("/*"); +// registration.setName("repeatableFilter"); +// registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE); +// return registration; +// } + +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/KaptchaTextCreator.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/KaptchaTextCreator.java new file mode 100644 index 00000000..7f8e1d50 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/KaptchaTextCreator.java @@ -0,0 +1,68 @@ +package com.ruoyi.framework.config; + +import java.util.Random; +import com.google.code.kaptcha.text.impl.DefaultTextCreator; + +/** + * 验证码文本生成器 + * + * @author ruoyi + */ +public class KaptchaTextCreator extends DefaultTextCreator +{ + private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(","); + + @Override + public String getText() + { + Integer result = 0; + Random random = new Random(); + int x = random.nextInt(10); + int y = random.nextInt(10); + StringBuilder suChinese = new StringBuilder(); + int randomoperands = random.nextInt(3); + if (randomoperands == 0) + { + result = x * y; + suChinese.append(CNUMBERS[x]); + suChinese.append("*"); + suChinese.append(CNUMBERS[y]); + } + else if (randomoperands == 1) + { + if ((x != 0) && y % x == 0) + { + result = y / x; + suChinese.append(CNUMBERS[y]); + suChinese.append("/"); + suChinese.append(CNUMBERS[x]); + } + else + { + result = x + y; + suChinese.append(CNUMBERS[x]); + suChinese.append("+"); + suChinese.append(CNUMBERS[y]); + } + } + else + { + if (x >= y) + { + result = x - y; + suChinese.append(CNUMBERS[x]); + suChinese.append("-"); + suChinese.append(CNUMBERS[y]); + } + else + { + result = y - x; + suChinese.append(CNUMBERS[y]); + suChinese.append("-"); + suChinese.append(CNUMBERS[x]); + } + } + suChinese.append("=?@" + result); + return suChinese.toString(); + } +} \ No newline at end of file diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java new file mode 100644 index 00000000..997a1013 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java @@ -0,0 +1,70 @@ +package com.ruoyi.framework.config; + +import com.baomidou.mybatisplus.annotation.DbType; +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import com.ruoyi.common.mybatis.handler.DefaultDBFieldHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +/** + * Mybatis Plus 配置 + * + * @author ruoyi + */ +@EnableTransactionManagement(proxyTargetClass = true) +@Configuration +public class MybatisPlusConfig +{ + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() + { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + // 分页插件 + interceptor.addInnerInterceptor(paginationInnerInterceptor()); + // 乐观锁插件 + interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor()); + // 阻断插件 + interceptor.addInnerInterceptor(blockAttackInnerInterceptor()); + return interceptor; + } + + /** + * 分页插件,自动识别数据库类型 https://baomidou.com/guide/interceptor-pagination.html + */ + public PaginationInnerInterceptor paginationInnerInterceptor() + { + PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(); + // 设置数据库类型为mysql + paginationInnerInterceptor.setDbType(DbType.MYSQL); + // 设置最大单页限制数量,默认 500 条,-1 不受限制 + paginationInnerInterceptor.setMaxLimit(-1L); + return paginationInnerInterceptor; + } + + /** + * 乐观锁插件 https://baomidou.com/guide/interceptor-optimistic-locker.html + */ + public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() + { + return new OptimisticLockerInnerInterceptor(); + } + + /** + * 如果是对全表的删除或更新操作,就会终止该操作 https://baomidou.com/guide/interceptor-block-attack.html + */ + public BlockAttackInnerInterceptor blockAttackInnerInterceptor() + { + return new BlockAttackInnerInterceptor(); + } + + @Bean + public MetaObjectHandler defaultMetaObjectHandler(){ + // 自动填充参数类 + return new DefaultDBFieldHandler(); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java new file mode 100644 index 00000000..3235c42a --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java @@ -0,0 +1,69 @@ +//package com.ruoyi.framework.config; +// +//import org.springframework.cache.annotation.CachingConfigurerSupport; +//import org.springframework.cache.annotation.EnableCaching; +//import org.springframework.context.annotation.Bean; +//import org.springframework.context.annotation.Configuration; +//import org.springframework.data.redis.connection.RedisConnectionFactory; +//import org.springframework.data.redis.core.RedisTemplate; +//import org.springframework.data.redis.core.script.DefaultRedisScript; +//import org.springframework.data.redis.serializer.StringRedisSerializer; +// +///** +// * redis配置 +// * +// * @author ruoyi +// */ +//@Configuration +//@EnableCaching +//public class RedisConfig extends CachingConfigurerSupport +//{ +// @Bean +// @SuppressWarnings(value = { "unchecked", "rawtypes" }) +// public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) +// { +// RedisTemplate template = new RedisTemplate<>(); +// template.setConnectionFactory(connectionFactory); +// +// FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class); +// +// // 使用StringRedisSerializer来序列化和反序列化redis的key值 +// template.setKeySerializer(new StringRedisSerializer()); +// template.setValueSerializer(serializer); +// +// // Hash的key也采用StringRedisSerializer的序列化方式 +// template.setHashKeySerializer(new StringRedisSerializer()); +// template.setHashValueSerializer(serializer); +// +// template.afterPropertiesSet(); +// return template; +// } +// +// @Bean +// public DefaultRedisScript limitScript() +// { +// DefaultRedisScript redisScript = new DefaultRedisScript<>(); +// redisScript.setScriptText(limitScriptText()); +// redisScript.setResultType(Long.class); +// return redisScript; +// } +// +// /** +// * 限流脚本 +// */ +// private String limitScriptText() +// { +// return "local key = KEYS[1]\n" + +// "local count = tonumber(ARGV[1])\n" + +// "local time = tonumber(ARGV[2])\n" + +// "local current = redis.call('get', key);\n" + +// "if current and tonumber(current) > count then\n" + +// " return tonumber(current);\n" + +// "end\n" + +// "current = redis.call('incr', key)\n" + +// "if tonumber(current) == 1 then\n" + +// " redis.call('expire', key, time)\n" + +// "end\n" + +// "return tonumber(current);"; +// } +//} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java new file mode 100644 index 00000000..26a7d4da --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java @@ -0,0 +1,71 @@ +package com.ruoyi.framework.config; + +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.CacheControl; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.concurrent.TimeUnit; + +/** + * 通用配置 + * + * @author ruoyi + */ +@Configuration +public class ResourcesConfig implements WebMvcConfigurer { + @Autowired + private RepeatSubmitInterceptor repeatSubmitInterceptor; + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + /** 本地文件上传路径 */ + registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**") + .addResourceLocations("file:" + RuoYiConfig.getProfile() + "/"); + + /** swagger配置 */ + registry.addResourceHandler("/swagger-ui/**") + .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/") + .setCacheControl(CacheControl.maxAge(5, TimeUnit.HOURS).cachePublic()); + ; + } + + /** + * 自定义拦截规则 + */ + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**"); + } + + /** + * 跨域配置 + */ + @Bean + public CorsFilter corsFilter() { + CorsConfiguration config = new CorsConfiguration(); + config.setAllowCredentials(true); + // 设置访问源地址 + config.addAllowedOriginPattern("*"); + // 设置访问源请求头 + config.addAllowedHeader("*"); + // 设置访问源请求方法 + config.addAllowedMethod("*"); + // 有效期 1800秒 + config.setMaxAge(1800L); + // 添加映射路径,拦截一切请求 + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", config); + // 返回新的CorsFilter + return new CorsFilter(source); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java new file mode 100644 index 00000000..ee972246 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java @@ -0,0 +1,204 @@ +package com.ruoyi.framework.config; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; +import com.ruoyi.framework.config.properties.PermitAllUrlProperties; +import com.ruoyi.framework.security.filter.JwtAuthenticationTokenFilter; +import com.ruoyi.framework.security.handle.AuthenticationEntryPointImpl; +import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.authentication.logout.LogoutFilter; +import org.springframework.web.filter.CorsFilter; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.mvc.method.RequestMappingInfo; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + +import javax.annotation.Resource; +import javax.annotation.security.PermitAll; +import java.util.Map; +import java.util.Set; + +/** + * spring security配置 + * + * @author ruoyi + */ +@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) +public class SecurityConfig extends WebSecurityConfigurerAdapter { + /** + * 自定义用户认证逻辑 + */ + @Autowired + private UserDetailsService userDetailsService; + + /** + * 认证失败处理类 + */ + @Autowired + private AuthenticationEntryPointImpl unauthorizedHandler; + + /** + * 退出处理类 + */ + @Autowired + private LogoutSuccessHandlerImpl logoutSuccessHandler; + + /** + * token认证过滤器 + */ + @Autowired + private JwtAuthenticationTokenFilter authenticationTokenFilter; + + /** + * 跨域过滤器 + */ + @Autowired + private CorsFilter corsFilter; + + /** + * 允许匿名访问的地址 + */ + @Autowired + private PermitAllUrlProperties permitAllUrl; + @Resource + private ApplicationContext applicationContext; + /** + * 解决 无法直接注入 AuthenticationManager + * + * @return + * @throws Exception + */ + @Bean + @Override + public AuthenticationManager authenticationManagerBean() throws Exception { + return super.authenticationManagerBean(); + } + + /** + * anyRequest | 匹配所有请求路径 + * access | SpringEl表达式结果为true时可以访问 + * anonymous | 匿名可以访问 + * denyAll | 用户不能访问 + * fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录) + * hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问 + * hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问 + * hasAuthority | 如果有参数,参数表示权限,则其权限可以访问 + * hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问 + * hasRole | 如果有参数,参数表示角色,则其角色可以访问 + * permitAll | 用户可以任意访问 + * rememberMe | 允许通过remember-me登录的用户访问 + * authenticated | 用户登录后可访问 + */ + @Override + protected void configure(HttpSecurity httpSecurity) throws Exception { + // 注解标记允许匿名访问的url + ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests(); + permitAllUrl.getUrls().forEach(url -> registry.antMatchers(url).permitAll()); + // 获得 @PermitAll 带来的 URL 列表,免登录 + Multimap permitAllUrls = getPermitAllUrlsFromAnnotations(); + httpSecurity + // CSRF禁用,因为不使用session + .csrf().disable() + // 禁用HTTP响应标头 + .headers().cacheControl().disable().and() + // 认证失败处理类 + .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and() + // 基于token,所以不需要session + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() + // 过滤请求 + .authorizeRequests() + // 对于登录login 注册register 验证码captchaImage 允许匿名访问 + .antMatchers("/login", "/register", "/captchaImage").permitAll() + // 静态资源,可匿名访问 + .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll() + .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**","/common/chatgpt").permitAll() + // 1.2 设置 @PermitAll 无需认证 + .antMatchers(HttpMethod.GET, permitAllUrls.get(HttpMethod.GET).toArray(new String[0])).permitAll() + .antMatchers(HttpMethod.POST, permitAllUrls.get(HttpMethod.POST).toArray(new String[0])).permitAll() + .antMatchers(HttpMethod.PUT, permitAllUrls.get(HttpMethod.PUT).toArray(new String[0])).permitAll() + .antMatchers(HttpMethod.DELETE, permitAllUrls.get(HttpMethod.DELETE).toArray(new String[0])).permitAll() + // websocket如果需要不登录也可以访问,需要在`SecurityConfig.java`中设置匿名访问 + .antMatchers("/websocket/**").permitAll() + //积木报表 + .antMatchers("/jmreport/**").permitAll() + // 任务回退接口 + .antMatchers("/bpm/task/back").permitAll() + // 除上面外的所有请求全部需要鉴权认证 + .anyRequest().authenticated() + .and() + .headers().frameOptions().disable(); + // 添加Logout filter + httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler); + // 添加JWT filter + httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); + // 添加CORS filter + httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class); + httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class); + } + + /** + * 强散列哈希加密实现 + */ + @Bean + public BCryptPasswordEncoder bCryptPasswordEncoder() { + return new BCryptPasswordEncoder(); + } + + /** + * 身份认证接口 + */ + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder()); + } + + private Multimap getPermitAllUrlsFromAnnotations() { + Multimap result = HashMultimap.create(); + // 获得接口对应的 HandlerMethod 集合 + RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping) + applicationContext.getBean("requestMappingHandlerMapping"); + Map handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods(); + // 获得有 @PermitAll 注解的接口 + for (Map.Entry entry : handlerMethodMap.entrySet()) { + HandlerMethod handlerMethod = entry.getValue(); + if (!handlerMethod.hasMethodAnnotation(PermitAll.class)) { + continue; + } + if (entry.getKey().getPatternsCondition() == null) { + continue; + } + Set urls = entry.getKey().getPatternsCondition().getPatterns(); + // 根据请求方法,添加到 result 结果 + entry.getKey().getMethodsCondition().getMethods().forEach(requestMethod -> { + switch (requestMethod) { + case GET: + result.putAll(HttpMethod.GET, urls); + break; + case POST: + result.putAll(HttpMethod.POST, urls); + break; + case PUT: + result.putAll(HttpMethod.PUT, urls); + break; + case DELETE: + result.putAll(HttpMethod.DELETE, urls); + break; + } + }); + } + return result; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ServerConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ServerConfig.java new file mode 100644 index 00000000..b5b7de31 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ServerConfig.java @@ -0,0 +1,32 @@ +package com.ruoyi.framework.config; + +import javax.servlet.http.HttpServletRequest; +import org.springframework.stereotype.Component; +import com.ruoyi.common.utils.ServletUtils; + +/** + * 服务相关配置 + * + * @author ruoyi + */ +@Component +public class ServerConfig +{ + /** + * 获取完整的请求路径,包括:域名,端口,上下文访问路径 + * + * @return 服务地址 + */ + public String getUrl() + { + HttpServletRequest request = ServletUtils.getRequest(); + return getDomain(request); + } + + public static String getDomain(HttpServletRequest request) + { + StringBuffer url = request.getRequestURL(); + String contextPath = request.getServletContext().getContextPath(); + return url.delete(url.length() - request.getRequestURI().length(), url.length()).append(contextPath).toString(); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java new file mode 100644 index 00000000..7840141e --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java @@ -0,0 +1,63 @@ +package com.ruoyi.framework.config; + +import com.ruoyi.common.utils.Threads; +import org.apache.commons.lang3.concurrent.BasicThreadFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadPoolExecutor; + +/** + * 线程池配置 + * + * @author ruoyi + **/ +@Configuration +public class ThreadPoolConfig +{ + // 核心线程池大小 + private int corePoolSize = 50; + + // 最大可创建的线程数 + private int maxPoolSize = 200; + + // 队列最大长度 + private int queueCapacity = 1000; + + // 线程池维护线程所允许的空闲时间 + private int keepAliveSeconds = 300; + + @Bean(name = "threadPoolTaskExecutor") + public ThreadPoolTaskExecutor threadPoolTaskExecutor() + { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setMaxPoolSize(maxPoolSize); + executor.setCorePoolSize(corePoolSize); + executor.setQueueCapacity(queueCapacity); + executor.setKeepAliveSeconds(keepAliveSeconds); + // 线程池对拒绝任务(无线程可用)的处理策略 + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + return executor; + } + + /** + * 执行周期性或定时任务 + */ + @Bean(name = "scheduledExecutorService") + protected ScheduledExecutorService scheduledExecutorService() + { + return new ScheduledThreadPoolExecutor(corePoolSize, + new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(), + new ThreadPoolExecutor.CallerRunsPolicy()) + { + @Override + protected void afterExecute(Runnable r, Throwable t) + { + super.afterExecute(r, t); + Threads.printException(r, t); + } + }; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java new file mode 100644 index 00000000..84f7e009 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java @@ -0,0 +1,77 @@ +package com.ruoyi.framework.config.properties; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import com.alibaba.druid.pool.DruidDataSource; + +/** + * druid 配置属性 + * + * @author ruoyi + */ +@Configuration +public class DruidProperties +{ + @Value("${spring.datasource.druid.initialSize}") + private int initialSize; + + @Value("${spring.datasource.druid.minIdle}") + private int minIdle; + + @Value("${spring.datasource.druid.maxActive}") + private int maxActive; + + @Value("${spring.datasource.druid.maxWait}") + private int maxWait; + + @Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}") + private int timeBetweenEvictionRunsMillis; + + @Value("${spring.datasource.druid.minEvictableIdleTimeMillis}") + private int minEvictableIdleTimeMillis; + + @Value("${spring.datasource.druid.maxEvictableIdleTimeMillis}") + private int maxEvictableIdleTimeMillis; + + @Value("${spring.datasource.druid.validationQuery}") + private String validationQuery; + + @Value("${spring.datasource.druid.testWhileIdle}") + private boolean testWhileIdle; + + @Value("${spring.datasource.druid.testOnBorrow}") + private boolean testOnBorrow; + + @Value("${spring.datasource.druid.testOnReturn}") + private boolean testOnReturn; + + public DruidDataSource dataSource(DruidDataSource datasource) + { + /** 配置初始化大小、最小、最大 */ + datasource.setInitialSize(initialSize); + datasource.setMaxActive(maxActive); + datasource.setMinIdle(minIdle); + + /** 配置获取连接等待超时的时间 */ + datasource.setMaxWait(maxWait); + + /** 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 */ + datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); + + /** 配置一个连接在池中最小、最大生存的时间,单位是毫秒 */ + datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); + datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis); + + /** + * 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。 + */ + datasource.setValidationQuery(validationQuery); + /** 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 */ + datasource.setTestWhileIdle(testWhileIdle); + /** 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */ + datasource.setTestOnBorrow(testOnBorrow); + /** 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */ + datasource.setTestOnReturn(testOnReturn); + return datasource; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/PermitAllUrlProperties.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/PermitAllUrlProperties.java new file mode 100644 index 00000000..9632eb1f --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/PermitAllUrlProperties.java @@ -0,0 +1,72 @@ +package com.ruoyi.framework.config.properties; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.regex.Pattern; +import org.apache.commons.lang3.RegExUtils; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.mvc.method.RequestMappingInfo; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; +import com.ruoyi.common.annotation.Anonymous; + +/** + * 设置Anonymous注解允许匿名访问的url + * + * @author ruoyi + */ +@Configuration +public class PermitAllUrlProperties implements InitializingBean, ApplicationContextAware +{ + private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}"); + + private ApplicationContext applicationContext; + + private List urls = new ArrayList<>(); + + public String ASTERISK = "*"; + + @Override + public void afterPropertiesSet() + { + RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class); + Map map = mapping.getHandlerMethods(); + + map.keySet().forEach(info -> { + HandlerMethod handlerMethod = map.get(info); + + // 获取方法上边的注解 替代path variable 为 * + Anonymous method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class); + Optional.ofNullable(method).ifPresent(anonymous -> info.getPatternsCondition().getPatterns() + .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK)))); + + // 获取类上边的注解, 替代path variable 为 * + Anonymous controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Anonymous.class); + Optional.ofNullable(controller).ifPresent(anonymous -> info.getPatternsCondition().getPatterns() + .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK)))); + }); + } + + @Override + public void setApplicationContext(ApplicationContext context) throws BeansException + { + this.applicationContext = context; + } + + public List getUrls() + { + return urls; + } + + public void setUrls(List urls) + { + this.urls = urls; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java new file mode 100644 index 00000000..e70b8cfa --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java @@ -0,0 +1,26 @@ +package com.ruoyi.framework.datasource; + +import java.util.Map; +import javax.sql.DataSource; +import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; + +/** + * 动态数据源 + * + * @author ruoyi + */ +public class DynamicDataSource extends AbstractRoutingDataSource +{ + public DynamicDataSource(DataSource defaultTargetDataSource, Map targetDataSources) + { + super.setDefaultTargetDataSource(defaultTargetDataSource); + super.setTargetDataSources(targetDataSources); + super.afterPropertiesSet(); + } + + @Override + protected Object determineCurrentLookupKey() + { + return DynamicDataSourceContextHolder.getDataSourceType(); + } +} \ No newline at end of file diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java new file mode 100644 index 00000000..3572db91 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java @@ -0,0 +1,45 @@ +package com.ruoyi.framework.datasource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 数据源切换处理 + * + * @author ruoyi + */ +public class DynamicDataSourceContextHolder +{ + public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class); + + /** + * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本, + * 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。 + */ + private static final ThreadLocal CONTEXT_HOLDER = new ThreadLocal<>(); + + /** + * 设置数据源的变量 + */ + public static void setDataSourceType(String dsType) + { + log.info("切换到{}数据源", dsType); + CONTEXT_HOLDER.set(dsType); + } + + /** + * 获得数据源的变量 + */ + public static String getDataSourceType() + { + return CONTEXT_HOLDER.get(); + } + + /** + * 清空数据源变量 + */ + public static void clearDataSourceType() + { + CONTEXT_HOLDER.remove(); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java new file mode 100644 index 00000000..4ddacaed --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java @@ -0,0 +1,55 @@ +package com.ruoyi.framework.interceptor; + +import java.lang.reflect.Method; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.stereotype.Component; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.HandlerInterceptor; +import com.alibaba.fastjson2.JSON; +import com.ruoyi.common.annotation.RepeatSubmit; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.ServletUtils; + +/** + * 防止重复提交拦截器 + * + * @author ruoyi + */ +@Component +public abstract class RepeatSubmitInterceptor implements HandlerInterceptor +{ + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception + { + if (handler instanceof HandlerMethod) + { + HandlerMethod handlerMethod = (HandlerMethod) handler; + Method method = handlerMethod.getMethod(); + RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class); + if (annotation != null) + { + if (this.isRepeatSubmit(request, annotation)) + { + AjaxResult ajaxResult = AjaxResult.error(annotation.message()); + ServletUtils.renderString(response, JSON.toJSONString(ajaxResult)); + return false; + } + } + return true; + } + else + { + return true; + } + } + + /** + * 验证是否重复提交由子类实现具体的防重复提交的规则 + * + * @param request + * @return + * @throws Exception + */ + public abstract boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation); +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java new file mode 100644 index 00000000..3160c054 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java @@ -0,0 +1,112 @@ +package com.ruoyi.framework.interceptor.impl; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import javax.servlet.http.HttpServletRequest; + +import com.ruoyi.common.utils.EhcacheUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import com.alibaba.fastjson2.JSON; +import com.ruoyi.common.annotation.RepeatSubmit; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.filter.RepeatedlyRequestWrapper; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.http.HttpHelper; +import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor; + +/** + * 判断请求url和数据是否和上一次相同, + * 如果和上次相同,则是重复提交表单。 有效时间为10秒内。 + * + * @author ruoyi + */ +@Component +public class SameUrlDataInterceptor extends RepeatSubmitInterceptor +{ + public final String REPEAT_PARAMS = "repeatParams"; + + public final String REPEAT_TIME = "repeatTime"; + + // 令牌自定义标识 + @Value("${token.header}") + private String header; + +// @Autowired +// private RedisCache redisCache; + + @SuppressWarnings("unchecked") + @Override + public boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation) + { + String nowParams = ""; + if (request instanceof RepeatedlyRequestWrapper) + { + RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request; + nowParams = HttpHelper.getBodyString(repeatedlyRequest); + } + + // body参数为空,获取Parameter的数据 + if (StringUtils.isEmpty(nowParams)) + { + nowParams = JSON.toJSONString(request.getParameterMap()); + } + Map nowDataMap = new HashMap(); + nowDataMap.put(REPEAT_PARAMS, nowParams); + nowDataMap.put(REPEAT_TIME, System.currentTimeMillis()); + + // 请求地址(作为存放cache的key值) + String url = request.getRequestURI(); + + // 唯一值(没有消息头则使用请求地址) + String submitKey = StringUtils.trimToEmpty(request.getHeader(header)); + + // 唯一标识(指定key + url + 消息头) + String cacheRepeatKey = CacheConstants.REPEAT_SUBMIT_KEY + url + submitKey; +// Object sessionObj = redisCache.getCacheObject(cacheRepeatKey); + Object sessionObj = EhcacheUtil.get("repeatsubmit",cacheRepeatKey); + if (sessionObj != null) + { + Map sessionMap = (Map) sessionObj; + if (sessionMap.containsKey(url)) + { + Map preDataMap = (Map) sessionMap.get(url); + if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap, annotation.interval())) + { + return true; + } + } + } + Map cacheMap = new HashMap(); + cacheMap.put(url, nowDataMap); + EhcacheUtil.put("repeatsubmit",cacheRepeatKey,cacheMap); +// redisCache.setCacheObject(cacheRepeatKey, cacheMap, annotation.interval(), TimeUnit.MILLISECONDS); + return false; + } + + /** + * 判断参数是否相同 + */ + private boolean compareParams(Map nowMap, Map preMap) + { + String nowParams = (String) nowMap.get(REPEAT_PARAMS); + String preParams = (String) preMap.get(REPEAT_PARAMS); + return nowParams.equals(preParams); + } + + /** + * 判断两次间隔时间 + */ + private boolean compareTime(Map nowMap, Map preMap, int interval) + { + long time1 = (Long) nowMap.get(REPEAT_TIME); + long time2 = (Long) preMap.get(REPEAT_TIME); + if ((time1 - time2) < interval) + { + return true; + } + return false; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/AsyncManager.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/AsyncManager.java new file mode 100644 index 00000000..7387a02c --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/AsyncManager.java @@ -0,0 +1,55 @@ +package com.ruoyi.framework.manager; + +import java.util.TimerTask; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import com.ruoyi.common.utils.Threads; +import com.ruoyi.common.utils.spring.SpringUtils; + +/** + * 异步任务管理器 + * + * @author ruoyi + */ +public class AsyncManager +{ + /** + * 操作延迟10毫秒 + */ + private final int OPERATE_DELAY_TIME = 10; + + /** + * 异步操作任务调度线程池 + */ + private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService"); + + /** + * 单例模式 + */ + private AsyncManager(){} + + private static AsyncManager me = new AsyncManager(); + + public static AsyncManager me() + { + return me; + } + + /** + * 执行任务 + * + * @param task 任务 + */ + public void execute(TimerTask task) + { + executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS); + } + + /** + * 停止任务线程池 + */ + public void shutdown() + { + Threads.shutdownAndAwaitTermination(executor); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java new file mode 100644 index 00000000..ec695cc9 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java @@ -0,0 +1,41 @@ +package com.ruoyi.framework.manager; + +import com.ruoyi.common.utils.EhcacheUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import javax.annotation.PreDestroy; + +/** + * 确保应用退出时能关闭后台线程 + * + * @author ruoyi + */ +@Component +public class ShutdownManager +{ + private static final Logger logger = LoggerFactory.getLogger("sys-user"); + + @PreDestroy + public void destroy() + { + shutdownAsyncManager(); + } + + /** + * 停止异步执行任务 + */ + private void shutdownAsyncManager() + { + try + { + logger.info("====关闭后台任务任务线程池===="); + EhcacheUtil.cacheManager.shutdown(); + AsyncManager.me().shutdown(); + } + catch (Exception e) + { + logger.error(e.getMessage(), e); + } + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java new file mode 100644 index 00000000..23d02308 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java @@ -0,0 +1,102 @@ +package com.ruoyi.framework.manager.factory; + +import java.util.TimerTask; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.LogUtils; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.ip.AddressUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.system.domain.SysLogininfor; +import com.ruoyi.system.domain.SysOperLog; +import com.ruoyi.system.service.ISysLogininforService; +import com.ruoyi.system.service.ISysOperLogService; +import eu.bitwalker.useragentutils.UserAgent; + +/** + * 异步工厂(产生任务用) + * + * @author ruoyi + */ +public class AsyncFactory +{ + private static final Logger sys_user_logger = LoggerFactory.getLogger("sys-user"); + + /** + * 记录登录信息 + * + * @param username 用户名 + * @param status 状态 + * @param message 消息 + * @param args 列表 + * @return 任务task + */ + public static TimerTask recordLogininfor(final String username, final String status, final String message, + final Object... args) + { + final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent")); + final String ip = IpUtils.getIpAddr(ServletUtils.getRequest()); + return new TimerTask() + { + @Override + public void run() + { + String address = AddressUtils.getRealAddressByIP(ip); + StringBuilder s = new StringBuilder(); + s.append(LogUtils.getBlock(ip)); + s.append(address); + s.append(LogUtils.getBlock(username)); + s.append(LogUtils.getBlock(status)); + s.append(LogUtils.getBlock(message)); + // 打印信息到日志 + sys_user_logger.info(s.toString(), args); + // 获取客户端操作系统 + String os = userAgent.getOperatingSystem().getName(); + // 获取客户端浏览器 + String browser = userAgent.getBrowser().getName(); + // 封装对象 + SysLogininfor logininfor = new SysLogininfor(); + logininfor.setUserName(username); + logininfor.setIpaddr(ip); + logininfor.setLoginLocation(address); + logininfor.setBrowser(browser); + logininfor.setOs(os); + logininfor.setMsg(message); + // 日志状态 + if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) + { + logininfor.setStatus(Constants.SUCCESS); + } + else if (Constants.LOGIN_FAIL.equals(status)) + { + logininfor.setStatus(Constants.FAIL); + } + // 插入数据 + SpringUtils.getBean(ISysLogininforService.class).insertLogininfor(logininfor); + } + }; + } + + /** + * 操作日志记录 + * + * @param operLog 操作日志信息 + * @return 任务task + */ + public static TimerTask recordOper(final SysOperLog operLog) + { + return new TimerTask() + { + @Override + public void run() + { + // 远程查询操作地点 + operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp())); + SpringUtils.getBean(ISysOperLogService.class).insertOperlog(operLog); + } + }; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/AuthenticationContextHolder.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/AuthenticationContextHolder.java new file mode 100644 index 00000000..2daa9e63 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/AuthenticationContextHolder.java @@ -0,0 +1,28 @@ +package com.ruoyi.framework.security.context; + +import org.springframework.security.core.Authentication; + +/** + * 身份验证信息 + * + * @author ruoyi + */ +public class AuthenticationContextHolder +{ + private static final ThreadLocal contextHolder = new ThreadLocal<>(); + + public static Authentication getContext() + { + return contextHolder.get(); + } + + public static void setContext(Authentication context) + { + contextHolder.set(context); + } + + public static void clearContext() + { + contextHolder.remove(); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/PermissionContextHolder.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/PermissionContextHolder.java new file mode 100644 index 00000000..f9766e52 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/PermissionContextHolder.java @@ -0,0 +1,27 @@ +package com.ruoyi.framework.security.context; + +import com.ruoyi.common.core.text.Convert; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; + +/** + * 权限信息 + * + * @author ruoyi + */ +public class PermissionContextHolder +{ + private static final String PERMISSION_CONTEXT_ATTRIBUTES = "PERMISSION_CONTEXT"; + + public static void setContext(String permission) + { + RequestContextHolder.currentRequestAttributes().setAttribute(PERMISSION_CONTEXT_ATTRIBUTES, permission, + RequestAttributes.SCOPE_REQUEST); + } + + public static String getContext() + { + return Convert.toStr(RequestContextHolder.currentRequestAttributes().getAttribute(PERMISSION_CONTEXT_ATTRIBUTES, + RequestAttributes.SCOPE_REQUEST)); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java new file mode 100644 index 00000000..3eb24954 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java @@ -0,0 +1,44 @@ +package com.ruoyi.framework.security.filter; + +import java.io.IOException; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.web.service.TokenService; + +/** + * token过滤器 验证token有效性 + * + * @author ruoyi + */ +@Component +public class JwtAuthenticationTokenFilter extends OncePerRequestFilter +{ + @Autowired + private TokenService tokenService; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) + throws ServletException, IOException + { + LoginUser loginUser = tokenService.getLoginUser(request); + if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())) + { + tokenService.verifyToken(loginUser); + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities()); + authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authenticationToken); + } + chain.doFilter(request, response); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java new file mode 100644 index 00000000..93b70325 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java @@ -0,0 +1,34 @@ +package com.ruoyi.framework.security.handle; + +import java.io.IOException; +import java.io.Serializable; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; +import com.alibaba.fastjson2.JSON; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; + +/** + * 认证失败处理类 返回未授权 + * + * @author ruoyi + */ +@Component +public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable +{ + private static final long serialVersionUID = -8970718410437077606L; + + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) + throws IOException + { + int code = HttpStatus.UNAUTHORIZED; + String msg = StringUtils.format("请求访问:{},认证失败,无法访问系统资源", request.getRequestURI()); + ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(code, msg))); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java new file mode 100644 index 00000000..6c2d4057 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java @@ -0,0 +1,50 @@ +package com.ruoyi.framework.security.handle; + +import com.alibaba.fastjson2.JSON; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.framework.web.service.TokenService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * 自定义退出处理类 返回成功 + * + * @author ruoyi + */ +@Configuration +public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler { + @Autowired + private TokenService tokenService; + + /** + * 退出处理 + * + * @return + */ + @Override + public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) + throws IOException, ServletException { + LoginUser loginUser = tokenService.getLoginUser(request); + if (StringUtils.isNotNull(loginUser)) { + String userName = loginUser.getUsername(); + // 删除用户缓存记录 + tokenService.delLoginUser(loginUser.getToken()); + // 记录用户退出日志 + AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, "退出成功")); + } + ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.success("退出成功"))); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/Server.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/Server.java new file mode 100644 index 00000000..63b03da7 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/Server.java @@ -0,0 +1,240 @@ +package com.ruoyi.framework.web.domain; + +import java.net.UnknownHostException; +import java.util.LinkedList; +import java.util.List; +import java.util.Properties; +import com.ruoyi.common.utils.Arith; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.framework.web.domain.server.Cpu; +import com.ruoyi.framework.web.domain.server.Jvm; +import com.ruoyi.framework.web.domain.server.Mem; +import com.ruoyi.framework.web.domain.server.Sys; +import com.ruoyi.framework.web.domain.server.SysFile; +import oshi.SystemInfo; +import oshi.hardware.CentralProcessor; +import oshi.hardware.CentralProcessor.TickType; +import oshi.hardware.GlobalMemory; +import oshi.hardware.HardwareAbstractionLayer; +import oshi.software.os.FileSystem; +import oshi.software.os.OSFileStore; +import oshi.software.os.OperatingSystem; +import oshi.util.Util; + +/** + * 服务器相关信息 + * + * @author ruoyi + */ +public class Server +{ + private static final int OSHI_WAIT_SECOND = 1000; + + /** + * CPU相关信息 + */ + private Cpu cpu = new Cpu(); + + /** + * 內存相关信息 + */ + private Mem mem = new Mem(); + + /** + * JVM相关信息 + */ + private Jvm jvm = new Jvm(); + + /** + * 服务器相关信息 + */ + private Sys sys = new Sys(); + + /** + * 磁盘相关信息 + */ + private List sysFiles = new LinkedList(); + + public Cpu getCpu() + { + return cpu; + } + + public void setCpu(Cpu cpu) + { + this.cpu = cpu; + } + + public Mem getMem() + { + return mem; + } + + public void setMem(Mem mem) + { + this.mem = mem; + } + + public Jvm getJvm() + { + return jvm; + } + + public void setJvm(Jvm jvm) + { + this.jvm = jvm; + } + + public Sys getSys() + { + return sys; + } + + public void setSys(Sys sys) + { + this.sys = sys; + } + + public List getSysFiles() + { + return sysFiles; + } + + public void setSysFiles(List sysFiles) + { + this.sysFiles = sysFiles; + } + + public void copyTo() throws Exception + { + SystemInfo si = new SystemInfo(); + HardwareAbstractionLayer hal = si.getHardware(); + + setCpuInfo(hal.getProcessor()); + + setMemInfo(hal.getMemory()); + + setSysInfo(); + + setJvmInfo(); + + setSysFiles(si.getOperatingSystem()); + } + + /** + * 设置CPU信息 + */ + private void setCpuInfo(CentralProcessor processor) + { + // CPU信息 + long[] prevTicks = processor.getSystemCpuLoadTicks(); + Util.sleep(OSHI_WAIT_SECOND); + long[] ticks = processor.getSystemCpuLoadTicks(); + long nice = ticks[TickType.NICE.getIndex()] - prevTicks[TickType.NICE.getIndex()]; + long irq = ticks[TickType.IRQ.getIndex()] - prevTicks[TickType.IRQ.getIndex()]; + long softirq = ticks[TickType.SOFTIRQ.getIndex()] - prevTicks[TickType.SOFTIRQ.getIndex()]; + long steal = ticks[TickType.STEAL.getIndex()] - prevTicks[TickType.STEAL.getIndex()]; + long cSys = ticks[TickType.SYSTEM.getIndex()] - prevTicks[TickType.SYSTEM.getIndex()]; + long user = ticks[TickType.USER.getIndex()] - prevTicks[TickType.USER.getIndex()]; + long iowait = ticks[TickType.IOWAIT.getIndex()] - prevTicks[TickType.IOWAIT.getIndex()]; + long idle = ticks[TickType.IDLE.getIndex()] - prevTicks[TickType.IDLE.getIndex()]; + long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal; + cpu.setCpuNum(processor.getLogicalProcessorCount()); + cpu.setTotal(totalCpu); + cpu.setSys(cSys); + cpu.setUsed(user); + cpu.setWait(iowait); + cpu.setFree(idle); + } + + /** + * 设置内存信息 + */ + private void setMemInfo(GlobalMemory memory) + { + mem.setTotal(memory.getTotal()); + mem.setUsed(memory.getTotal() - memory.getAvailable()); + mem.setFree(memory.getAvailable()); + } + + /** + * 设置服务器信息 + */ + private void setSysInfo() + { + Properties props = System.getProperties(); + sys.setComputerName(IpUtils.getHostName()); + sys.setComputerIp(IpUtils.getHostIp()); + sys.setOsName(props.getProperty("os.name")); + sys.setOsArch(props.getProperty("os.arch")); + sys.setUserDir(props.getProperty("user.dir")); + } + + /** + * 设置Java虚拟机 + */ + private void setJvmInfo() throws UnknownHostException + { + Properties props = System.getProperties(); + jvm.setTotal(Runtime.getRuntime().totalMemory()); + jvm.setMax(Runtime.getRuntime().maxMemory()); + jvm.setFree(Runtime.getRuntime().freeMemory()); + jvm.setVersion(props.getProperty("java.version")); + jvm.setHome(props.getProperty("java.home")); + } + + /** + * 设置磁盘信息 + */ + private void setSysFiles(OperatingSystem os) + { + FileSystem fileSystem = os.getFileSystem(); + List fsArray = fileSystem.getFileStores(); + for (OSFileStore fs : fsArray) + { + long free = fs.getUsableSpace(); + long total = fs.getTotalSpace(); + long used = total - free; + SysFile sysFile = new SysFile(); + sysFile.setDirName(fs.getMount()); + sysFile.setSysTypeName(fs.getType()); + sysFile.setTypeName(fs.getName()); + sysFile.setTotal(convertFileSize(total)); + sysFile.setFree(convertFileSize(free)); + sysFile.setUsed(convertFileSize(used)); + sysFile.setUsage(Arith.mul(Arith.div(used, total, 4), 100)); + sysFiles.add(sysFile); + } + } + + /** + * 字节转换 + * + * @param size 字节大小 + * @return 转换后值 + */ + public String convertFileSize(long size) + { + long kb = 1024; + long mb = kb * 1024; + long gb = mb * 1024; + if (size >= gb) + { + return String.format("%.1f GB", (float) size / gb); + } + else if (size >= mb) + { + float f = (float) size / mb; + return String.format(f > 100 ? "%.0f MB" : "%.1f MB", f); + } + else if (size >= kb) + { + float f = (float) size / kb; + return String.format(f > 100 ? "%.0f KB" : "%.1f KB", f); + } + else + { + return String.format("%d B", size); + } + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Cpu.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Cpu.java new file mode 100644 index 00000000..a13a66cf --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Cpu.java @@ -0,0 +1,101 @@ +package com.ruoyi.framework.web.domain.server; + +import com.ruoyi.common.utils.Arith; + +/** + * CPU相关信息 + * + * @author ruoyi + */ +public class Cpu +{ + /** + * 核心数 + */ + private int cpuNum; + + /** + * CPU总的使用率 + */ + private double total; + + /** + * CPU系统使用率 + */ + private double sys; + + /** + * CPU用户使用率 + */ + private double used; + + /** + * CPU当前等待率 + */ + private double wait; + + /** + * CPU当前空闲率 + */ + private double free; + + public int getCpuNum() + { + return cpuNum; + } + + public void setCpuNum(int cpuNum) + { + this.cpuNum = cpuNum; + } + + public double getTotal() + { + return Arith.round(Arith.mul(total, 100), 2); + } + + public void setTotal(double total) + { + this.total = total; + } + + public double getSys() + { + return Arith.round(Arith.mul(sys / total, 100), 2); + } + + public void setSys(double sys) + { + this.sys = sys; + } + + public double getUsed() + { + return Arith.round(Arith.mul(used / total, 100), 2); + } + + public void setUsed(double used) + { + this.used = used; + } + + public double getWait() + { + return Arith.round(Arith.mul(wait / total, 100), 2); + } + + public void setWait(double wait) + { + this.wait = wait; + } + + public double getFree() + { + return Arith.round(Arith.mul(free / total, 100), 2); + } + + public void setFree(double free) + { + this.free = free; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Jvm.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Jvm.java new file mode 100644 index 00000000..444b280d --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Jvm.java @@ -0,0 +1,130 @@ +package com.ruoyi.framework.web.domain.server; + +import java.lang.management.ManagementFactory; +import com.ruoyi.common.utils.Arith; +import com.ruoyi.common.utils.DateUtils; + +/** + * JVM相关信息 + * + * @author ruoyi + */ +public class Jvm +{ + /** + * 当前JVM占用的内存总数(M) + */ + private double total; + + /** + * JVM最大可用内存总数(M) + */ + private double max; + + /** + * JVM空闲内存(M) + */ + private double free; + + /** + * JDK版本 + */ + private String version; + + /** + * JDK路径 + */ + private String home; + + public double getTotal() + { + return Arith.div(total, (1024 * 1024), 2); + } + + public void setTotal(double total) + { + this.total = total; + } + + public double getMax() + { + return Arith.div(max, (1024 * 1024), 2); + } + + public void setMax(double max) + { + this.max = max; + } + + public double getFree() + { + return Arith.div(free, (1024 * 1024), 2); + } + + public void setFree(double free) + { + this.free = free; + } + + public double getUsed() + { + return Arith.div(total - free, (1024 * 1024), 2); + } + + public double getUsage() + { + return Arith.mul(Arith.div(total - free, total, 4), 100); + } + + /** + * 获取JDK名称 + */ + public String getName() + { + return ManagementFactory.getRuntimeMXBean().getVmName(); + } + + public String getVersion() + { + return version; + } + + public void setVersion(String version) + { + this.version = version; + } + + public String getHome() + { + return home; + } + + public void setHome(String home) + { + this.home = home; + } + + /** + * JDK启动时间 + */ + public String getStartTime() + { + return DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, DateUtils.getServerStartDate()); + } + + /** + * JDK运行时间 + */ + public String getRunTime() + { + return DateUtils.getDatePoor(DateUtils.getNowDate(), DateUtils.getServerStartDate()); + } + + /** + * 运行参数 + */ + public String getInputArgs() + { + return ManagementFactory.getRuntimeMXBean().getInputArguments().toString(); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Mem.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Mem.java new file mode 100644 index 00000000..13eec521 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Mem.java @@ -0,0 +1,61 @@ +package com.ruoyi.framework.web.domain.server; + +import com.ruoyi.common.utils.Arith; + +/** + * 內存相关信息 + * + * @author ruoyi + */ +public class Mem +{ + /** + * 内存总量 + */ + private double total; + + /** + * 已用内存 + */ + private double used; + + /** + * 剩余内存 + */ + private double free; + + public double getTotal() + { + return Arith.div(total, (1024 * 1024 * 1024), 2); + } + + public void setTotal(long total) + { + this.total = total; + } + + public double getUsed() + { + return Arith.div(used, (1024 * 1024 * 1024), 2); + } + + public void setUsed(long used) + { + this.used = used; + } + + public double getFree() + { + return Arith.div(free, (1024 * 1024 * 1024), 2); + } + + public void setFree(long free) + { + this.free = free; + } + + public double getUsage() + { + return Arith.mul(Arith.div(used, total, 4), 100); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Sys.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Sys.java new file mode 100644 index 00000000..45d64d9c --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Sys.java @@ -0,0 +1,84 @@ +package com.ruoyi.framework.web.domain.server; + +/** + * 系统相关信息 + * + * @author ruoyi + */ +public class Sys +{ + /** + * 服务器名称 + */ + private String computerName; + + /** + * 服务器Ip + */ + private String computerIp; + + /** + * 项目路径 + */ + private String userDir; + + /** + * 操作系统 + */ + private String osName; + + /** + * 系统架构 + */ + private String osArch; + + public String getComputerName() + { + return computerName; + } + + public void setComputerName(String computerName) + { + this.computerName = computerName; + } + + public String getComputerIp() + { + return computerIp; + } + + public void setComputerIp(String computerIp) + { + this.computerIp = computerIp; + } + + public String getUserDir() + { + return userDir; + } + + public void setUserDir(String userDir) + { + this.userDir = userDir; + } + + public String getOsName() + { + return osName; + } + + public void setOsName(String osName) + { + this.osName = osName; + } + + public String getOsArch() + { + return osArch; + } + + public void setOsArch(String osArch) + { + this.osArch = osArch; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/SysFile.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/SysFile.java new file mode 100644 index 00000000..1320cde6 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/SysFile.java @@ -0,0 +1,114 @@ +package com.ruoyi.framework.web.domain.server; + +/** + * 系统文件相关信息 + * + * @author ruoyi + */ +public class SysFile +{ + /** + * 盘符路径 + */ + private String dirName; + + /** + * 盘符类型 + */ + private String sysTypeName; + + /** + * 文件类型 + */ + private String typeName; + + /** + * 总大小 + */ + private String total; + + /** + * 剩余大小 + */ + private String free; + + /** + * 已经使用量 + */ + private String used; + + /** + * 资源的使用率 + */ + private double usage; + + public String getDirName() + { + return dirName; + } + + public void setDirName(String dirName) + { + this.dirName = dirName; + } + + public String getSysTypeName() + { + return sysTypeName; + } + + public void setSysTypeName(String sysTypeName) + { + this.sysTypeName = sysTypeName; + } + + public String getTypeName() + { + return typeName; + } + + public void setTypeName(String typeName) + { + this.typeName = typeName; + } + + public String getTotal() + { + return total; + } + + public void setTotal(String total) + { + this.total = total; + } + + public String getFree() + { + return free; + } + + public void setFree(String free) + { + this.free = free; + } + + public String getUsed() + { + return used; + } + + public void setUsed(String used) + { + this.used = used; + } + + public double getUsage() + { + return usage; + } + + public void setUsage(double usage) + { + this.usage = usage; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java new file mode 100644 index 00000000..51dd8c54 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java @@ -0,0 +1,114 @@ +package com.ruoyi.framework.web.exception; + +import javax.servlet.http.HttpServletRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.validation.BindException; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.exception.DemoModeException; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.StringUtils; + +/** + * 全局异常处理器 + * + * @author ruoyi + */ +@RestControllerAdvice +public class GlobalExceptionHandler +{ + private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); + + /** + * 权限校验异常 + */ + @ExceptionHandler(AccessDeniedException.class) + public AjaxResult handleAccessDeniedException(AccessDeniedException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',权限校验失败'{}'", requestURI, e.getMessage()); + return AjaxResult.error(HttpStatus.FORBIDDEN, "没有权限,请联系管理员授权"); + } + + /** + * 请求方式不支持 + */ + @ExceptionHandler(HttpRequestMethodNotSupportedException.class) + public AjaxResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e, + HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod()); + return AjaxResult.error(e.getMessage()); + } + + /** + * 业务异常 + */ + @ExceptionHandler(ServiceException.class) + public AjaxResult handleServiceException(ServiceException e, HttpServletRequest request) + { + log.error(e.getMessage(), e); + Integer code = e.getCode(); + return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage()); + } + + /** + * 拦截未知的运行时异常 + */ + @ExceptionHandler(RuntimeException.class) + public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生未知异常.", requestURI, e); + return AjaxResult.error(e.getMessage()); + } + + /** + * 系统异常 + */ + @ExceptionHandler(Exception.class) + public AjaxResult handleException(Exception e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生系统异常.", requestURI, e); + return AjaxResult.error(e.getMessage()); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(BindException.class) + public AjaxResult handleBindException(BindException e) + { + log.error(e.getMessage(), e); + String message = e.getAllErrors().get(0).getDefaultMessage(); + return AjaxResult.error(message); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) + { + log.error(e.getMessage(), e); + String message = e.getBindingResult().getFieldError().getDefaultMessage(); + return AjaxResult.error(message); + } + + /** + * 演示模式异常 + */ + @ExceptionHandler(DemoModeException.class) + public AjaxResult handleDemoModeException(DemoModeException e) + { + return AjaxResult.error("演示模式,不允许操作"); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/DataScopeService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/DataScopeService.java new file mode 100644 index 00000000..88b9050d --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/DataScopeService.java @@ -0,0 +1,163 @@ +package com.ruoyi.framework.web.service; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.ruoyi.common.core.domain.entity.SysDept; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.enums.CommonStatusEnum; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.aspectj.DataScopeAspect; +import com.ruoyi.system.domain.SysRoleDept; +import com.ruoyi.system.domain.SysUserRole; +import com.ruoyi.system.mapper.SysRoleDeptMapper; +import com.ruoyi.system.mapper.SysUserRoleMapper; +import com.ruoyi.system.service.ISysDeptService; +import com.ruoyi.system.service.ISysRoleService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * 数据权限范围操作类 + * @author wangzongrun + */ + +@Component +public class DataScopeService { + + @Autowired + private SysUserRoleMapper userRoleMapper; + + @Autowired + private SysRoleDeptMapper sysRoleDeptMapper; + + @Autowired + private ISysRoleService sysRoleService; + + @Autowired + private ISysDeptService sysDeptService; + + /** + * 根据用户id集合获取数据范围id集合 + * + * @param userId 用户id集合 + * @param orgId 机构id + * @return 数据范围id集合 + * @author wangzongrun + */ + public List getUserDataScopeIdList(Long userId, Long orgId){ + List roleIdList = CollectionUtil.newArrayList(); + // 获取用户所有角色 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysUserRole::getUserId, userId); + userRoleMapper.selectList(queryWrapper).forEach(sysUserRole -> roleIdList.add(sysUserRole.getRoleId())); + // 获取这些角色对应的数据范围 + if (ObjectUtil.isNotEmpty(roleIdList)) { + return getUserDataScopeIdList(roleIdList, orgId); + } + return CollectionUtil.newArrayList(); + } + + /** + * 根据角色id集合获取数据范围id集合 + * + * @param roleIdList 角色id集合 + * @param orgId 机构id + * @return 数据范围id集合 + */ + public List getUserDataScopeIdList(List roleIdList, Long orgId) { + Set resultList = CollectionUtil.newHashSet(); + + //定义角色中最大数据范围的类型,目前系统按最大范围策略来,如果你同时拥有ALL和SELF的权限,系统最后按ALL返回 + int strongerDataScopeType = Integer.parseInt(DataScopeAspect.DATA_SCOPE_SELF); + + //获取用户自定义数据范围的角色集合 + List customDataScopeRoleIdList = CollectionUtil.newArrayList(); + if (ObjectUtil.isNotEmpty(roleIdList)) { + List sysRoleList = sysRoleService.listByIds(roleIdList); + for (SysRole sysRole : sysRoleList) { + if (DataScopeAspect.DATA_SCOPE_CUSTOM.equals(sysRole.getDataScope())) { + customDataScopeRoleIdList.add(sysRole.getRoleId()); + } else { + if (Integer.parseInt(sysRole.getDataScope()) <= strongerDataScopeType) { + strongerDataScopeType = Integer.parseInt(sysRole.getDataScope()); + } + } + } + } + + //自定义数据范围的角色对应的数据范围 + List roleDataScopeIdList = getRoleDataScopeIdList(customDataScopeRoleIdList); + + //角色中拥有最大数据范围类型的数据范围 + List dataScopeIdList = getDataScopeListByDataScopeType(strongerDataScopeType, orgId); + + resultList.addAll(dataScopeIdList); + resultList.addAll(roleDataScopeIdList); + return CollectionUtil.newArrayList(resultList); + } + + /** + * 根据角色id获取角色数据范围集合 + * + * @param roleIdList 角色id集合 + * @return 数据范围id集合 + */ + List getRoleDataScopeIdList(List roleIdList){ + List resultList = CollectionUtil.newArrayList(); + if (ObjectUtil.isNotEmpty(roleIdList)) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(SysRoleDept::getRoleId, roleIdList); + sysRoleDeptMapper.selectList(queryWrapper).forEach(sysRoleDataScope -> resultList.add(sysRoleDataScope.getDeptId())); + } + return resultList; + } + + /** + * 根据数据范围类型获取当前登录用户的数据范围id集合 + * + * @param dataScopeType 数据范围类型(1全部数据 2本部门及以下数据 3本部门数据 4仅本人数据) + * @param orgId 组织机构id + * @return 数据范围id集合 + */ + public List getDataScopeListByDataScopeType(Integer dataScopeType, Long orgId) { + List resultList = CollectionUtil.newArrayList(); + + if (ObjectUtil.isEmpty(orgId)) { + return CollectionUtil.newArrayList(); + } + + // 如果是范围类型是全部数据,则获取当前系统所有的组织架构id + switch (dataScopeType.toString()) { + case DataScopeAspect.DATA_SCOPE_ALL: { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysDept::getStatus, CommonStatusEnum.ENABLE.getStatus()); + List list = sysDeptService.list(queryWrapper); + resultList = list.stream().map(SysDept::getDeptId).collect(Collectors.toList()); + break; + } + // 如果范围类型是本部门及以下部门,则查询本节点和子节点集合,包含本节点 + case DataScopeAspect.DATA_SCOPE_DEPT_AND_CHILD: { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysDept::getStatus, CommonStatusEnum.ENABLE.getStatus()); + String format = StringUtils.format(" AND dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )", orgId, orgId); + queryWrapper.last(format); + List list = sysDeptService.list(queryWrapper); + resultList = list.stream().map(SysDept::getDeptId).collect(Collectors.toList()); + break; + } + // 如果数据范围是本部门,不含子节点,则直接返回本部门 + case DataScopeAspect.DATA_SCOPE_SELF: + resultList.add(orgId); + break; + default: + break; + } + return resultList; + } + +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/JimuReportTokenService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/JimuReportTokenService.java new file mode 100644 index 00000000..0f2a57ad --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/JimuReportTokenService.java @@ -0,0 +1,47 @@ +package com.ruoyi.framework.web.service; + + +import cn.hutool.core.bean.BeanUtil; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.utils.StringUtils; +import org.jeecg.modules.jmreport.api.JmReportTokenServiceI; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import java.util.Map; + +/** + * 积木报表权限认证 + */ +@Component +public class JimuReportTokenService implements JmReportTokenServiceI { + @Autowired + TokenService tokenService; + + @Override + public String getUsername(String token) { + LoginUser loginUser = tokenService.getLoginUser(token); + return loginUser.getUsername(); + } + + @Override + public Boolean verifyToken(String token) { + LoginUser loginUser = tokenService.getLoginUser(token); + if (StringUtils.isNotNull(loginUser) && (loginUser.getTenantId() == 1 && "admin".equals(loginUser.getUsername()))) { + tokenService.verifyToken(loginUser); + return true; + } + return false; + } + + @Override + public String getToken(HttpServletRequest request) { + return tokenService.getToken(request); + } + + @Override + public Map getUserInfo(String token) { + return BeanUtil.beanToMap(tokenService.getLoginUser(token)); + } +} \ No newline at end of file diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java new file mode 100644 index 00000000..dcd3e469 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java @@ -0,0 +1,169 @@ +package com.ruoyi.framework.web.service; + +import java.util.Set; + +import com.ruoyi.framework.security.context.PermissionContextHolder; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; + +/** + * RuoYi首创 自定义权限实现,ss取自SpringSecurity首字母 + * + * @author ruoyi + */ +@Service("ss") +public class PermissionService +{ + /** 所有权限标识 */ + private static final String ALL_PERMISSION = "*:*:*"; + + /** 管理员角色权限标识 */ + private static final String SUPER_ADMIN = "admin"; + + private static final String ROLE_DELIMETER = ","; + + private static final String PERMISSION_DELIMETER = ","; + + /** + * 验证用户是否具备某权限 + * + * @param permission 权限字符串 + * @return 用户是否具备某权限 + */ + public boolean hasPermi(String permission) + { + if (StringUtils.isEmpty(permission)) + { + return false; + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) + { + return false; + } + PermissionContextHolder.setContext(permission); + return hasPermissions(loginUser.getPermissions(), permission); + } + + /** + * 验证用户是否不具备某权限,与 hasPermi逻辑相反 + * + * @param permission 权限字符串 + * @return 用户是否不具备某权限 + */ + public boolean lacksPermi(String permission) + { + return hasPermi(permission) != true; + } + + /** + * 验证用户是否具有以下任意一个权限 + * + * @param permissions 以 PERMISSION_NAMES_DELIMETER 为分隔符的权限列表 + * @return 用户是否具有以下任意一个权限 + */ + public boolean hasAnyPermi(String permissions) + { + if (StringUtils.isEmpty(permissions)) + { + return false; + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) + { + return false; + } + PermissionContextHolder.setContext(permissions); + Set authorities = loginUser.getPermissions(); + for (String permission : permissions.split(PERMISSION_DELIMETER)) + { + if (permission != null && hasPermissions(authorities, permission)) + { + return true; + } + } + return false; + } + + /** + * 判断用户是否拥有某个角色 + * + * @param role 角色字符串 + * @return 用户是否具备某角色 + */ + public boolean hasRole(String role) + { + if (StringUtils.isEmpty(role)) + { + return false; + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) + { + return false; + } + for (SysRole sysRole : loginUser.getUser().getRoles()) + { + String roleKey = sysRole.getRoleKey(); + if (SUPER_ADMIN.equals(roleKey) || roleKey.equals(StringUtils.trim(role))) + { + return true; + } + } + return false; + } + + /** + * 验证用户是否不具备某角色,与 isRole逻辑相反。 + * + * @param role 角色名称 + * @return 用户是否不具备某角色 + */ + public boolean lacksRole(String role) + { + return hasRole(role) != true; + } + + /** + * 验证用户是否具有以下任意一个角色 + * + * @param roles 以 ROLE_NAMES_DELIMETER 为分隔符的角色列表 + * @return 用户是否具有以下任意一个角色 + */ + public boolean hasAnyRoles(String roles) + { + if (StringUtils.isEmpty(roles)) + { + return false; + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) + { + return false; + } + for (String role : roles.split(ROLE_DELIMETER)) + { + if (hasRole(role)) + { + return true; + } + } + return false; + } + + /** + * 判断是否包含权限 + * + * @param permissions 权限列表 + * @param permission 权限字符串 + * @return 用户是否具备某权限 + */ + private boolean hasPermissions(Set permissions, String permission) + { + return permissions.contains(ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission)); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java new file mode 100644 index 00000000..07a523ee --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java @@ -0,0 +1,136 @@ +package com.ruoyi.framework.web.service; + +import javax.annotation.Resource; + +import com.ruoyi.common.utils.*; +import com.ruoyi.framework.security.context.AuthenticationContextHolder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Component; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.exception.user.CaptchaException; +import com.ruoyi.common.exception.user.CaptchaExpireException; +import com.ruoyi.common.exception.user.UserPasswordNotMatchException; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.system.service.ISysUserService; + +/** + * 登录校验方法 + * + * @author ruoyi + */ +@Component +public class SysLoginService +{ + @Autowired + private TokenService tokenService; + + @Resource + private AuthenticationManager authenticationManager; + + @Autowired + private ISysUserService userService; + + @Autowired + private ISysConfigService configService; + + /** + * 登录验证 + * + * @param username 用户名 + * @param password 密码 + * @param code 验证码 + * @param uuid 唯一标识 + * @return 结果 + */ + public String login(String username, String password, String code, String uuid) + { + boolean captchaEnabled = configService.selectCaptchaEnabled(); + // 验证码开关 + if (captchaEnabled) + { + validateCaptcha(username, code, uuid); + } + // 用户验证 + Authentication authentication = null; + try + { + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password); + AuthenticationContextHolder.setContext(authenticationToken); + // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername + authentication = authenticationManager.authenticate(authenticationToken); + } + catch (Exception e) + { + if (e instanceof BadCredentialsException) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"))); + throw new UserPasswordNotMatchException(); + } + else + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage())); + throw new ServiceException(e.getMessage()); + } + } + finally + { + AuthenticationContextHolder.clearContext(); + } + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"))); + LoginUser loginUser = (LoginUser) authentication.getPrincipal(); + recordLoginInfo(loginUser.getUserId()); + // 生成token + return tokenService.createToken(loginUser); + } + + /** + * 校验验证码 + * + * @param username 用户名 + * @param code 验证码 + * @param uuid 唯一标识 + * @return 结果 + */ + public void validateCaptcha(String username, String code, String uuid) + { + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, ""); + Object captcha = EhcacheUtil.get("verify", verifyKey); +// redisCache.deleteObject(verifyKey); + if (captcha == null) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"))); + throw new CaptchaExpireException(); + } + EhcacheUtil.remove("verify", verifyKey); + if (!code.equalsIgnoreCase(captcha.toString())) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"))); + throw new CaptchaException(); + } + } + + /** + * 记录登录信息 + * + * @param userId 用户ID + */ + public void recordLoginInfo(Long userId) + { + SysUser sysUser = new SysUser(); + sysUser.setUserId(userId); + sysUser.setLoginIp(IpUtils.getIpAddr(ServletUtils.getRequest())); + sysUser.setLoginDate(DateUtils.getNowDate()); + userService.updateUserProfile(sysUser); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPasswordService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPasswordService.java new file mode 100644 index 00000000..c01f6e6a --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPasswordService.java @@ -0,0 +1,98 @@ +package com.ruoyi.framework.web.service; + +import java.util.concurrent.TimeUnit; + +import com.ruoyi.common.utils.EhcacheUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Component; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.exception.user.UserPasswordNotMatchException; +import com.ruoyi.common.exception.user.UserPasswordRetryLimitExceedException; +import com.ruoyi.common.utils.MessageUtils; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.framework.security.context.AuthenticationContextHolder; + +/** + * 登录密码方法 + * + * @author ruoyi + */ +@Component +public class SysPasswordService +{ +// @Autowired +// private RedisCache redisCache; + + @Value(value = "${user.password.maxRetryCount}") + private int maxRetryCount; + + @Value(value = "${user.password.lockTime}") + private int lockTime; + + /** + * 登录账户密码错误次数缓存键名 + * + * @param username 用户名 + * @return 缓存键key + */ + private String getCacheKey(String username) + { + return CacheConstants.PWD_ERR_CNT_KEY + username; + } + + public void validate(SysUser user) + { + Authentication usernamePasswordAuthenticationToken = AuthenticationContextHolder.getContext(); + String username = usernamePasswordAuthenticationToken.getName(); + String password = usernamePasswordAuthenticationToken.getCredentials().toString(); + + Object obj = EhcacheUtil.get("lock", getCacheKey(username)); + Integer retryCount = 0; + if (obj != null) + { + retryCount = Integer.valueOf(obj.toString()); + } + + if (retryCount >= Integer.valueOf(maxRetryCount).intValue()) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, + MessageUtils.message("user.password.retry.limit.exceed", maxRetryCount, lockTime))); + throw new UserPasswordRetryLimitExceedException(maxRetryCount, lockTime); + } + + if (!matches(user, password)) + { + retryCount = retryCount + 1; + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, + MessageUtils.message("user.password.retry.limit.count", retryCount))); + EhcacheUtil.put("lock", getCacheKey(username), retryCount); +// redisCache.setCacheObject(getCacheKey(username), retryCount, lockTime, TimeUnit.MINUTES); + throw new UserPasswordNotMatchException(); + } + else + { + clearLoginRecordCache(username); + } + } + + public boolean matches(SysUser user, String rawPassword) + { + return SecurityUtils.matchesPassword(rawPassword, user.getPassword()); + } + + public void clearLoginRecordCache(String loginName) + { + Object lock = EhcacheUtil.get("lock", getCacheKey(loginName)); + if (lock!= null) + { + EhcacheUtil.remove("lock", getCacheKey(loginName)); +// redisCache.deleteObject(getCacheKey(loginName)); + } + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPermissionService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPermissionService.java new file mode 100644 index 00000000..8fb4866f --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPermissionService.java @@ -0,0 +1,83 @@ +package com.ruoyi.framework.web.service; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import com.ruoyi.common.core.domain.entity.SysRole; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.system.service.ISysMenuService; +import com.ruoyi.system.service.ISysRoleService; + +/** + * 用户权限处理 + * + * @author ruoyi + */ +@Component +public class SysPermissionService +{ + @Autowired + private ISysRoleService roleService; + + @Autowired + private ISysMenuService menuService; + + /** + * 获取角色数据权限 + * + * @param user 用户信息 + * @return 角色权限信息 + */ + public Set getRolePermission(SysUser user) + { + Set roles = new HashSet(); + // 管理员拥有所有权限 + if (user.isAdmin()) + { + roles.add("admin"); + } + else + { + roles.addAll(roleService.selectRolePermissionByUserId(user.getUserId())); + } + return roles; + } + + /** + * 获取菜单数据权限 + * + * @param user 用户信息 + * @return 菜单权限信息 + */ + public Set getMenuPermission(SysUser user) + { + Set perms = new HashSet(); + // 管理员拥有所有权限 + if (user.isAdmin()) + { + perms.add("*:*:*"); + } + else + { + List roles = user.getRoles(); + if (!roles.isEmpty() && roles.size() > 1) + { + // 多角色设置permissions属性,以便数据权限匹配权限 + for (SysRole role : roles) + { + Set rolePerms = menuService.selectMenuPermsByRoleId(role.getRoleId()); + role.setPermissions(rolePerms); + perms.addAll(rolePerms); + } + } + else + { + perms.addAll(menuService.selectMenuPermsByUserId(user.getUserId())); + } + } + return perms; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java new file mode 100644 index 00000000..0736204d --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java @@ -0,0 +1,95 @@ +package com.ruoyi.framework.web.service; + +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.RegisterBody; +import com.ruoyi.common.exception.user.CaptchaException; +import com.ruoyi.common.exception.user.CaptchaExpireException; +import com.ruoyi.common.utils.EhcacheUtil; +import com.ruoyi.common.utils.MessageUtils; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.system.service.ISysUserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * 注册校验方法 + * + * @author ruoyi + */ +@Component +public class SysRegisterService { + @Autowired + private ISysUserService userService; + + @Autowired + private ISysConfigService configService; + +// @Autowired +// private RedisCache redisCache; + + /** + * 注册 + */ + public String register(RegisterBody registerBody) { + String msg = "", username = registerBody.getUsername(), password = registerBody.getPassword(); + SysUser sysUser = new SysUser(); + sysUser.setUserName(username); + boolean captchaEnabled = configService.selectCaptchaEnabled(); + // 验证码开关 + if (captchaEnabled) { + validateCaptcha(username, registerBody.getCode(), registerBody.getUuid()); + } + + if (StringUtils.isEmpty(username)) { + msg = "用户名不能为空"; + } else if (StringUtils.isEmpty(password)) { + msg = "用户密码不能为空"; + } else if (username.length() < UserConstants.USERNAME_MIN_LENGTH + || username.length() > UserConstants.USERNAME_MAX_LENGTH) { + msg = "账户长度必须在2到20个字符之间"; + } else if (password.length() < UserConstants.PASSWORD_MIN_LENGTH + || password.length() > UserConstants.PASSWORD_MAX_LENGTH) { + msg = "密码长度必须在5到20个字符之间"; + } else if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(sysUser))) { + msg = "保存用户'" + username + "'失败,注册账号已存在"; + } else { + sysUser.setNickName(username); + sysUser.setPassword(SecurityUtils.encryptPassword(password)); + boolean regFlag = userService.registerUser(sysUser); + if (!regFlag) { + msg = "注册失败,请联系系统管理人员"; + } else { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.register.success"))); + } + } + return msg; + } + + /** + * 校验验证码 + * + * @param username 用户名 + * @param code 验证码 + * @param uuid 唯一标识 + * @return 结果 + */ + public void validateCaptcha(String username, String code, String uuid) { + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, ""); + Object obj = EhcacheUtil.get("verify", verifyKey); +// String captcha = redisCache.getCacheObject(verifyKey); +// redisCache.deleteObject(verifyKey); + if (obj == null) { + throw new CaptchaExpireException(); + } + if (!code.equalsIgnoreCase(obj.toString())) { + throw new CaptchaException(); + } + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java new file mode 100644 index 00000000..1bedfbf3 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java @@ -0,0 +1,264 @@ +package com.ruoyi.framework.web.service; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import javax.servlet.http.HttpServletRequest; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; +import com.ruoyi.common.utils.EhcacheUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.ip.AddressUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.common.utils.uuid.IdUtils; +import eu.bitwalker.useragentutils.UserAgent; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; + +/** + * token验证处理 + * + * @author ruoyi + */ +@Component +public class TokenService +{ + // 令牌自定义标识 + @Value("${token.header}") + private String header; + + // 令牌秘钥 + @Value("${token.secret}") + private String secret; + + // 令牌有效期(默认30分钟) + @Value("${token.expireTime}") + private int expireTime; + + protected static final long MILLIS_SECOND = 1000; + + protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND; + + private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L; + +// @Autowired +// private RedisCache redisCache; + + /** + * 获取用户身份信息 + * + * @return 用户信息 + */ + public LoginUser getLoginUser(HttpServletRequest request) + { + // 获取请求携带的令牌 + String token = getToken(request); + if (StringUtils.isNotEmpty(token)) + { + try + { + Claims claims = parseToken(token); + // 解析对应的权限以及用户信息 + String uuid = (String) claims.get(Constants.LOGIN_USER_KEY); + String userKey = getTokenKey(uuid); + Object obj = EhcacheUtil.get("login", userKey); + if (obj == null) { + return null; + } + LoginUser user = JSONUtil.toBean(JSONUtil.toJsonStr(obj), LoginUser.class); +// LoginUser user = redisCache.getCacheObject(userKey); + return user; + } + catch (Exception e) + { + } + } + return null; + } + public LoginUser getLoginUser(String token) + { + // 获取请求携带的令牌 + if (StringUtils.isNotEmpty(token)) + { + try + { + Claims claims = parseToken(token); + // 解析对应的权限以及用户信息 + String uuid = (String) claims.get(Constants.LOGIN_USER_KEY); + String userKey = getTokenKey(uuid); + Object obj = EhcacheUtil.get("login", userKey); + if (obj == null) { + return null; + } + LoginUser user = JSONUtil.toBean(JSONUtil.toJsonStr(obj), LoginUser.class); +// LoginUser user = redisCache.getCacheObject(userKey); + return user; + } + catch (Exception e) + { + } + } + return null; + } + /** + * 设置用户身份信息 + */ + public void setLoginUser(LoginUser loginUser) + { + if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken())) + { + refreshToken(loginUser); + } + } + + /** + * 删除用户身份信息 + */ + public void delLoginUser(String token) + { + if (StringUtils.isNotEmpty(token)) + { + String userKey = getTokenKey(token); + EhcacheUtil.remove("login", userKey); +// redisCache.deleteObject(userKey); + } + } + + /** + * 创建令牌 + * + * @param loginUser 用户信息 + * @return 令牌 + */ + public String createToken(LoginUser loginUser) + { + String token = IdUtils.fastUUID(); + loginUser.setToken(token); + setUserAgent(loginUser); + refreshToken(loginUser); + + Map claims = new HashMap<>(); + claims.put(Constants.LOGIN_USER_KEY, token); + return createToken(claims); + } + + /** + * 验证令牌有效期,相差不足20分钟,自动刷新缓存 + * + * @param loginUser + * @return 令牌 + */ + public void verifyToken(LoginUser loginUser) + { + long expireTime = loginUser.getExpireTime(); + long currentTime = System.currentTimeMillis(); + if (expireTime - currentTime <= MILLIS_MINUTE_TEN) + { + refreshToken(loginUser); + } + } + + /** + * 刷新令牌有效期 + * + * @param loginUser 登录信息 + */ + public void refreshToken(LoginUser loginUser) + { + loginUser.setLoginTime(System.currentTimeMillis()); + loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE); + // 根据uuid将loginUser缓存 + String userKey = getTokenKey(loginUser.getToken()); + EhcacheUtil.put("login", userKey,loginUser); +// redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES); + } + + /** + * 设置用户代理信息 + * + * @param loginUser 登录信息 + */ + public void setUserAgent(LoginUser loginUser) + { + UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent")); + String ip = IpUtils.getIpAddr(ServletUtils.getRequest()); + loginUser.setIpaddr(ip); + loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); + loginUser.setBrowser(userAgent.getBrowser().getName()); + loginUser.setOs(userAgent.getOperatingSystem().getName()); + } + + /** + * 从数据声明生成令牌 + * + * @param claims 数据声明 + * @return 令牌 + */ + private String createToken(Map claims) + { + String token = Jwts.builder() + .setClaims(claims) + .signWith(SignatureAlgorithm.HS512, secret).compact(); + return token; + } + + /** + * 从令牌中获取数据声明 + * + * @param token 令牌 + * @return 数据声明 + */ + private Claims parseToken(String token) + { + return Jwts.parser() + .setSigningKey(secret) + .parseClaimsJws(token) + .getBody(); + } + + /** + * 从令牌中获取用户名 + * + * @param token 令牌 + * @return 用户名 + */ + public String getUsernameFromToken(String token) + { + Claims claims = parseToken(token); + return claims.getSubject(); + } + + /** + * 获取请求token + * + * @param request + * @return token + */ + public String getToken(HttpServletRequest request) { + String token = request.getParameter("token"); + if (StrUtil.isBlank(token)) { + token = request.getHeader("token"); + } + if (StrUtil.isBlank(token)) { + token = request.getHeader(header); + } + if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) { + token = token.replace(Constants.TOKEN_PREFIX, ""); + } + return token; + } + + private String getTokenKey(String uuid) + { + return CacheConstants.LOGIN_TOKEN_KEY + uuid; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java new file mode 100644 index 00000000..c9d8b1a9 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java @@ -0,0 +1,71 @@ +package com.ruoyi.framework.web.service; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.enums.UserStatus; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.service.ISysUserService; + +import java.util.List; + +/** + * 用户验证处理 + * + * @author ruoyi + */ +@Service +public class UserDetailsServiceImpl implements UserDetailsService +{ + private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class); + + @Autowired + private ISysUserService userService; + + @Autowired + private SysPermissionService permissionService; + + @Autowired + private SysPasswordService passwordService; + + @Autowired + private DataScopeService dataScopeService; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException + { + SysUser user = userService.selectUserByUserName(username); + if (StringUtils.isNull(user)) + { + log.info("登录用户:{} 不存在.", username); + throw new ServiceException("登录用户:" + username + " 不存在"); + } + else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) + { + log.info("登录用户:{} 已被删除.", username); + throw new ServiceException("对不起,您的账号:" + username + " 已被删除"); + } + else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) + { + log.info("登录用户:{} 已被停用.", username); + throw new ServiceException("对不起,您的账号:" + username + " 已停用"); + } + passwordService.validate(user); + + return createLoginUser(user); + } + + public UserDetails createLoginUser(SysUser user) + { + //查询数据权限范围 + List userDataScopeIdList = dataScopeService.getUserDataScopeIdList(user.getUserId(), user.getDeptId()); + return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user),userDataScopeIdList,user.getTenantId()); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/websocket/SemaphoreUtils.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/websocket/SemaphoreUtils.java new file mode 100644 index 00000000..57567f30 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/websocket/SemaphoreUtils.java @@ -0,0 +1,58 @@ +package com.ruoyi.framework.websocket; + +import java.util.concurrent.Semaphore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 信号量相关处理 + * + * @author ruoyi + */ +public class SemaphoreUtils +{ + /** + * SemaphoreUtils 日志控制器 + */ + private static final Logger LOGGER = LoggerFactory.getLogger(SemaphoreUtils.class); + + /** + * 获取信号量 + * + * @param semaphore + * @return + */ + public static boolean tryAcquire(Semaphore semaphore) + { + boolean flag = false; + + try + { + flag = semaphore.tryAcquire(); + } + catch (Exception e) + { + LOGGER.error("获取信号量异常", e); + } + + return flag; + } + + /** + * 释放信号量 + * + * @param semaphore + */ + public static void release(Semaphore semaphore) + { + + try + { + semaphore.release(); + } + catch (Exception e) + { + LOGGER.error("释放信号量异常", e); + } + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/websocket/WebSocketConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/websocket/WebSocketConfig.java new file mode 100644 index 00000000..be63f35f --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/websocket/WebSocketConfig.java @@ -0,0 +1,20 @@ +package com.ruoyi.framework.websocket; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.socket.server.standard.ServerEndpointExporter; + +/** + * websocket 配置 + * + * @author ruoyi + */ +@Configuration +public class WebSocketConfig +{ + @Bean + public ServerEndpointExporter serverEndpointExporter() + { + return new ServerEndpointExporter(); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/websocket/WebSocketServer.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/websocket/WebSocketServer.java new file mode 100644 index 00000000..49c0d165 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/websocket/WebSocketServer.java @@ -0,0 +1,103 @@ +package com.ruoyi.framework.websocket; + +import java.util.concurrent.Semaphore; +import javax.websocket.OnClose; +import javax.websocket.OnError; +import javax.websocket.OnMessage; +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.ServerEndpoint; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +/** + * websocket 消息处理 + * + * @author ruoyi + */ +@Component +@ServerEndpoint("/websocket/message") +public class WebSocketServer +{ + /** + * WebSocketServer 日志控制器 + */ + private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketServer.class); + + /** + * 默认最多允许同时在线人数100 + */ + public static int socketMaxOnlineCount = 100; + + private static Semaphore socketSemaphore = new Semaphore(socketMaxOnlineCount); + + /** + * 连接建立成功调用的方法 + */ + @OnOpen + public void onOpen(Session session) throws Exception + { + boolean semaphoreFlag = false; + // 尝试获取信号量 + semaphoreFlag = SemaphoreUtils.tryAcquire(socketSemaphore); + if (!semaphoreFlag) + { + // 未获取到信号量 + LOGGER.error("\n 当前在线人数超过限制数- {}", socketMaxOnlineCount); + WebSocketUsers.sendMessageToUserByText(session, "当前在线人数超过限制数:" + socketMaxOnlineCount); + session.close(); + } + else + { + // 添加用户 + WebSocketUsers.put(session.getId(), session); + LOGGER.info("\n 建立连接 - {}", session); + LOGGER.info("\n 当前人数 - {}", WebSocketUsers.getUsers().size()); + WebSocketUsers.sendMessageToUserByText(session, "连接成功"); + } + } + + /** + * 连接关闭时处理 + */ + @OnClose + public void onClose(Session session) + { + LOGGER.info("\n 关闭连接 - {}", session); + // 移除用户 + WebSocketUsers.remove(session.getId()); + // 获取到信号量则需释放 + SemaphoreUtils.release(socketSemaphore); + } + + /** + * 抛出异常时处理 + */ + @OnError + public void onError(Session session, Throwable exception) throws Exception + { + if (session.isOpen()) + { + // 关闭连接 + session.close(); + } + String sessionId = session.getId(); + LOGGER.info("\n 连接异常 - {}", sessionId); + LOGGER.info("\n 异常信息 - {}", exception); + // 移出用户 + WebSocketUsers.remove(sessionId); + // 获取到信号量则需释放 + SemaphoreUtils.release(socketSemaphore); + } + + /** + * 服务器接收到客户端消息时调用的方法 + */ + @OnMessage + public void onMessage(String message, Session session) + { + String msg = message.replace("你", "我").replace("吗", ""); + WebSocketUsers.sendMessageToUserByText(session, msg); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/websocket/WebSocketUsers.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/websocket/WebSocketUsers.java new file mode 100644 index 00000000..81e549ec --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/websocket/WebSocketUsers.java @@ -0,0 +1,140 @@ +package com.ruoyi.framework.websocket; + +import java.io.IOException; +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import javax.websocket.Session; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * websocket 客户端用户集 + * + * @author ruoyi + */ +public class WebSocketUsers +{ + /** + * WebSocketUsers 日志控制器 + */ + private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketUsers.class); + + /** + * 用户集 + */ + private static Map USERS = new ConcurrentHashMap(); + + /** + * 存储用户 + * + * @param key 唯一键 + * @param session 用户信息 + */ + public static void put(String key, Session session) + { + USERS.put(key, session); + } + + /** + * 移除用户 + * + * @param session 用户信息 + * + * @return 移除结果 + */ + public static boolean remove(Session session) + { + String key = null; + boolean flag = USERS.containsValue(session); + if (flag) + { + Set> entries = USERS.entrySet(); + for (Map.Entry entry : entries) + { + Session value = entry.getValue(); + if (value.equals(session)) + { + key = entry.getKey(); + break; + } + } + } + else + { + return true; + } + return remove(key); + } + + /** + * 移出用户 + * + * @param key 键 + */ + public static boolean remove(String key) + { + LOGGER.info("\n 正在移出用户 - {}", key); + Session remove = USERS.remove(key); + if (remove != null) + { + boolean containsValue = USERS.containsValue(remove); + LOGGER.info("\n 移出结果 - {}", containsValue ? "失败" : "成功"); + return containsValue; + } + else + { + return true; + } + } + + /** + * 获取在线用户列表 + * + * @return 返回用户集合 + */ + public static Map getUsers() + { + return USERS; + } + + /** + * 群发消息文本消息 + * + * @param message 消息内容 + */ + public static void sendMessageToUsersByText(String message) + { + Collection values = USERS.values(); + for (Session value : values) + { + sendMessageToUserByText(value, message); + } + } + + /** + * 发送文本消息 + * + * @param userName 自己的用户名 + * @param message 消息内容 + */ + public static void sendMessageToUserByText(Session session, String message) + { + if (session != null) + { + try + { + session.getBasicRemote().sendText(message); + } + catch (IOException e) + { + LOGGER.error("\n[发送消息异常]", e); + } + } + else + { + LOGGER.info("\n[你已离线]"); + } + } +} diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/aspectj/DataScopeAspect.class b/ruoyi-framework/target/classes/com/ruoyi/framework/aspectj/DataScopeAspect.class new file mode 100644 index 00000000..f3b9babc Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/aspectj/DataScopeAspect.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/aspectj/DataSourceAspect.class b/ruoyi-framework/target/classes/com/ruoyi/framework/aspectj/DataSourceAspect.class new file mode 100644 index 00000000..4b69b4bf Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/aspectj/DataSourceAspect.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/aspectj/LogAspect.class b/ruoyi-framework/target/classes/com/ruoyi/framework/aspectj/LogAspect.class new file mode 100644 index 00000000..2571526c Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/aspectj/LogAspect.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/config/ApplicationConfig.class b/ruoyi-framework/target/classes/com/ruoyi/framework/config/ApplicationConfig.class new file mode 100644 index 00000000..7f0b206b Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/config/ApplicationConfig.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/config/CaptchaConfig.class b/ruoyi-framework/target/classes/com/ruoyi/framework/config/CaptchaConfig.class new file mode 100644 index 00000000..eb4e93e8 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/config/CaptchaConfig.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/config/DruidConfig$1.class b/ruoyi-framework/target/classes/com/ruoyi/framework/config/DruidConfig$1.class new file mode 100644 index 00000000..4ad12cdc Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/config/DruidConfig$1.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/config/DruidConfig.class b/ruoyi-framework/target/classes/com/ruoyi/framework/config/DruidConfig.class new file mode 100644 index 00000000..9bf6f523 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/config/DruidConfig.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/config/FilterConfig.class b/ruoyi-framework/target/classes/com/ruoyi/framework/config/FilterConfig.class new file mode 100644 index 00000000..72ca705c Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/config/FilterConfig.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/config/KaptchaTextCreator.class b/ruoyi-framework/target/classes/com/ruoyi/framework/config/KaptchaTextCreator.class new file mode 100644 index 00000000..0adf02f5 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/config/KaptchaTextCreator.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/config/MybatisPlusConfig.class b/ruoyi-framework/target/classes/com/ruoyi/framework/config/MybatisPlusConfig.class new file mode 100644 index 00000000..3c9e99fb Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/config/MybatisPlusConfig.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/config/ResourcesConfig.class b/ruoyi-framework/target/classes/com/ruoyi/framework/config/ResourcesConfig.class new file mode 100644 index 00000000..5d831212 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/config/ResourcesConfig.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/config/SecurityConfig$1.class b/ruoyi-framework/target/classes/com/ruoyi/framework/config/SecurityConfig$1.class new file mode 100644 index 00000000..d44deb5f Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/config/SecurityConfig$1.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/config/SecurityConfig.class b/ruoyi-framework/target/classes/com/ruoyi/framework/config/SecurityConfig.class new file mode 100644 index 00000000..36639213 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/config/SecurityConfig.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/config/ServerConfig.class b/ruoyi-framework/target/classes/com/ruoyi/framework/config/ServerConfig.class new file mode 100644 index 00000000..7814bfa9 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/config/ServerConfig.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/config/ThreadPoolConfig$1.class b/ruoyi-framework/target/classes/com/ruoyi/framework/config/ThreadPoolConfig$1.class new file mode 100644 index 00000000..32d0c39b Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/config/ThreadPoolConfig$1.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/config/ThreadPoolConfig.class b/ruoyi-framework/target/classes/com/ruoyi/framework/config/ThreadPoolConfig.class new file mode 100644 index 00000000..a8bfca7a Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/config/ThreadPoolConfig.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/config/properties/DruidProperties.class b/ruoyi-framework/target/classes/com/ruoyi/framework/config/properties/DruidProperties.class new file mode 100644 index 00000000..d1e8cbe3 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/config/properties/DruidProperties.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/config/properties/PermitAllUrlProperties.class b/ruoyi-framework/target/classes/com/ruoyi/framework/config/properties/PermitAllUrlProperties.class new file mode 100644 index 00000000..fdf29a67 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/config/properties/PermitAllUrlProperties.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/datasource/DynamicDataSource.class b/ruoyi-framework/target/classes/com/ruoyi/framework/datasource/DynamicDataSource.class new file mode 100644 index 00000000..0091a911 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/datasource/DynamicDataSource.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.class b/ruoyi-framework/target/classes/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.class new file mode 100644 index 00000000..0ce5fe6f Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.class b/ruoyi-framework/target/classes/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.class new file mode 100644 index 00000000..e289a900 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.class b/ruoyi-framework/target/classes/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.class new file mode 100644 index 00000000..ac2daead Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/manager/AsyncManager.class b/ruoyi-framework/target/classes/com/ruoyi/framework/manager/AsyncManager.class new file mode 100644 index 00000000..0fa509e9 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/manager/AsyncManager.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/manager/ShutdownManager.class b/ruoyi-framework/target/classes/com/ruoyi/framework/manager/ShutdownManager.class new file mode 100644 index 00000000..d2087c92 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/manager/ShutdownManager.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/manager/factory/AsyncFactory$1.class b/ruoyi-framework/target/classes/com/ruoyi/framework/manager/factory/AsyncFactory$1.class new file mode 100644 index 00000000..7556caa3 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/manager/factory/AsyncFactory$1.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/manager/factory/AsyncFactory$2.class b/ruoyi-framework/target/classes/com/ruoyi/framework/manager/factory/AsyncFactory$2.class new file mode 100644 index 00000000..d1463168 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/manager/factory/AsyncFactory$2.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/manager/factory/AsyncFactory.class b/ruoyi-framework/target/classes/com/ruoyi/framework/manager/factory/AsyncFactory.class new file mode 100644 index 00000000..a0fe17cb Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/manager/factory/AsyncFactory.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/security/context/AuthenticationContextHolder.class b/ruoyi-framework/target/classes/com/ruoyi/framework/security/context/AuthenticationContextHolder.class new file mode 100644 index 00000000..a3213b81 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/security/context/AuthenticationContextHolder.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/security/context/PermissionContextHolder.class b/ruoyi-framework/target/classes/com/ruoyi/framework/security/context/PermissionContextHolder.class new file mode 100644 index 00000000..df80f21a Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/security/context/PermissionContextHolder.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.class b/ruoyi-framework/target/classes/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.class new file mode 100644 index 00000000..b49866b3 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.class b/ruoyi-framework/target/classes/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.class new file mode 100644 index 00000000..fcb85d87 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.class b/ruoyi-framework/target/classes/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.class new file mode 100644 index 00000000..4e5d9564 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/web/domain/Server.class b/ruoyi-framework/target/classes/com/ruoyi/framework/web/domain/Server.class new file mode 100644 index 00000000..ecc282e6 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/web/domain/Server.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/web/domain/server/Cpu.class b/ruoyi-framework/target/classes/com/ruoyi/framework/web/domain/server/Cpu.class new file mode 100644 index 00000000..a1d1316d Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/web/domain/server/Cpu.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/web/domain/server/Jvm.class b/ruoyi-framework/target/classes/com/ruoyi/framework/web/domain/server/Jvm.class new file mode 100644 index 00000000..c2b12628 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/web/domain/server/Jvm.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/web/domain/server/Mem.class b/ruoyi-framework/target/classes/com/ruoyi/framework/web/domain/server/Mem.class new file mode 100644 index 00000000..c810f6d8 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/web/domain/server/Mem.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/web/domain/server/Sys.class b/ruoyi-framework/target/classes/com/ruoyi/framework/web/domain/server/Sys.class new file mode 100644 index 00000000..45623d04 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/web/domain/server/Sys.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/web/domain/server/SysFile.class b/ruoyi-framework/target/classes/com/ruoyi/framework/web/domain/server/SysFile.class new file mode 100644 index 00000000..2b70dfc0 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/web/domain/server/SysFile.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/web/exception/GlobalExceptionHandler.class b/ruoyi-framework/target/classes/com/ruoyi/framework/web/exception/GlobalExceptionHandler.class new file mode 100644 index 00000000..ea0be7f0 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/web/exception/GlobalExceptionHandler.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/web/service/DataScopeService.class b/ruoyi-framework/target/classes/com/ruoyi/framework/web/service/DataScopeService.class new file mode 100644 index 00000000..0d350958 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/web/service/DataScopeService.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/web/service/JimuReportTokenService.class b/ruoyi-framework/target/classes/com/ruoyi/framework/web/service/JimuReportTokenService.class new file mode 100644 index 00000000..eb53e5e7 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/web/service/JimuReportTokenService.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/web/service/PermissionService.class b/ruoyi-framework/target/classes/com/ruoyi/framework/web/service/PermissionService.class new file mode 100644 index 00000000..fb3e9b38 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/web/service/PermissionService.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/web/service/SysLoginService.class b/ruoyi-framework/target/classes/com/ruoyi/framework/web/service/SysLoginService.class new file mode 100644 index 00000000..010536a8 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/web/service/SysLoginService.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/web/service/SysPasswordService.class b/ruoyi-framework/target/classes/com/ruoyi/framework/web/service/SysPasswordService.class new file mode 100644 index 00000000..08f2e6ac Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/web/service/SysPasswordService.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/web/service/SysPermissionService.class b/ruoyi-framework/target/classes/com/ruoyi/framework/web/service/SysPermissionService.class new file mode 100644 index 00000000..c598dbf6 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/web/service/SysPermissionService.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/web/service/SysRegisterService.class b/ruoyi-framework/target/classes/com/ruoyi/framework/web/service/SysRegisterService.class new file mode 100644 index 00000000..152d871f Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/web/service/SysRegisterService.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/web/service/TokenService.class b/ruoyi-framework/target/classes/com/ruoyi/framework/web/service/TokenService.class new file mode 100644 index 00000000..f4c43043 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/web/service/TokenService.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/web/service/UserDetailsServiceImpl.class b/ruoyi-framework/target/classes/com/ruoyi/framework/web/service/UserDetailsServiceImpl.class new file mode 100644 index 00000000..644d3313 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/web/service/UserDetailsServiceImpl.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/websocket/SemaphoreUtils.class b/ruoyi-framework/target/classes/com/ruoyi/framework/websocket/SemaphoreUtils.class new file mode 100644 index 00000000..0d2b37f9 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/websocket/SemaphoreUtils.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/websocket/WebSocketConfig.class b/ruoyi-framework/target/classes/com/ruoyi/framework/websocket/WebSocketConfig.class new file mode 100644 index 00000000..37d4ea54 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/websocket/WebSocketConfig.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/websocket/WebSocketServer.class b/ruoyi-framework/target/classes/com/ruoyi/framework/websocket/WebSocketServer.class new file mode 100644 index 00000000..2fbc53c7 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/websocket/WebSocketServer.class differ diff --git a/ruoyi-framework/target/classes/com/ruoyi/framework/websocket/WebSocketUsers.class b/ruoyi-framework/target/classes/com/ruoyi/framework/websocket/WebSocketUsers.class new file mode 100644 index 00000000..f45d2d67 Binary files /dev/null and b/ruoyi-framework/target/classes/com/ruoyi/framework/websocket/WebSocketUsers.class differ diff --git a/ruoyi-generator/pom.xml b/ruoyi-generator/pom.xml new file mode 100644 index 00000000..f570d810 --- /dev/null +++ b/ruoyi-generator/pom.xml @@ -0,0 +1,46 @@ + + + + ruoyi + com.ruoyi + 3.8.4 + + 4.0.0 + + ruoyi-generator + + + generator代码生成 + + + + + + + org.apache.velocity + velocity-engine-core + + + + + commons-collections + commons-collections + + + + + com.ruoyi + ruoyi-common + + + + + com.baomidou + mybatis-plus-generator + + + + + diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/config/GenConfig.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/config/GenConfig.java new file mode 100644 index 00000000..cc4cd14c --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/config/GenConfig.java @@ -0,0 +1,73 @@ +package com.ruoyi.generator.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.PropertySource; +import org.springframework.stereotype.Component; + +/** + * 读取代码生成相关配置 + * + * @author ruoyi + */ +@Component +@ConfigurationProperties(prefix = "gen") +@PropertySource(value = { "classpath:generator.yml" }) +public class GenConfig +{ + /** 作者 */ + public static String author; + + /** 生成包路径 */ + public static String packageName; + + /** 自动去除表前缀,默认是false */ + public static boolean autoRemovePre; + + /** 表前缀(类名不会包含表前缀) */ + public static String tablePrefix; + + public static String getAuthor() + { + return author; + } + + @Value("${author}") + public void setAuthor(String author) + { + GenConfig.author = author; + } + + public static String getPackageName() + { + return packageName; + } + + @Value("${packageName}") + public void setPackageName(String packageName) + { + GenConfig.packageName = packageName; + } + + public static boolean getAutoRemovePre() + { + return autoRemovePre; + } + + @Value("${autoRemovePre}") + public void setAutoRemovePre(boolean autoRemovePre) + { + GenConfig.autoRemovePre = autoRemovePre; + } + + public static String getTablePrefix() + { + return tablePrefix; + } + + @Value("${tablePrefix}") + public void setTablePrefix(String tablePrefix) + { + GenConfig.tablePrefix = tablePrefix; + } +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/controller/GenController.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/controller/GenController.java new file mode 100644 index 00000000..1354eb1a --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/controller/GenController.java @@ -0,0 +1,194 @@ +package com.ruoyi.generator.controller; + +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.generator.domain.GenTable; +import com.ruoyi.generator.domain.GenTableColumn; +import com.ruoyi.generator.service.IGenTableColumnService; +import com.ruoyi.generator.service.IGenTableService; +import org.apache.commons.io.IOUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 代码生成 操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/tool/gen") +public class GenController extends BaseController { + @Autowired + private IGenTableService genTableService; + + @Autowired + private IGenTableColumnService genTableColumnService; + + /** + * 查询代码生成列表 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:list')") + @GetMapping("/list") + public TableDataInfo genList(GenTable genTable) { + startPage(); + List list = genTableService.selectGenTableList(genTable); + return getDataTable(list); + } + + /** + * 修改代码生成业务 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:query')") + @GetMapping(value = "/{tableId}") + public AjaxResult getInfo(@PathVariable Long tableId) { + GenTable table = genTableService.selectGenTableById(tableId); + List tables = genTableService.selectGenTableAll(); + List list = genTableColumnService.selectGenTableColumnListByTableId(tableId); + Map map = new HashMap(); + map.put("info", table); + map.put("rows", list); + map.put("tables", tables); + return success(map); + } + + /** + * 查询数据库列表 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:list')") + @GetMapping("/db/list") + public TableDataInfo dataList(GenTable genTable) { + startPage(); + List list = genTableService.selectDbTableList(genTable); + return getDataTable(list); + } + + /** + * 查询数据表字段列表 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:list')") + @GetMapping(value = "/column/{tableId}") + public TableDataInfo columnList(Long tableId) { + TableDataInfo dataInfo = new TableDataInfo(); + List list = genTableColumnService.selectGenTableColumnListByTableId(tableId); + dataInfo.setRows(list); + dataInfo.setTotal(list.size()); + return dataInfo; + } + + /** + * 导入表结构(保存) + */ + @PreAuthorize("@ss.hasPermi('tool:gen:import')") + @Log(title = "代码生成", businessType = BusinessType.IMPORT) + @PostMapping("/importTable") + public AjaxResult importTableSave(String tables) { + String[] tableNames = Convert.toStrArray(tables); + // 查询表信息 + List tableList = genTableService.selectDbTableListByNames(tableNames); + genTableService.importGenTable(tableList); + return success(); + } + + /** + * 修改保存代码生成业务 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:edit')") + @Log(title = "代码生成", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult editSave(@Validated @RequestBody GenTable genTable) { + genTableService.validateEdit(genTable); + genTableService.updateGenTable(genTable); + return success(); + } + + /** + * 删除代码生成 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:remove')") + @Log(title = "代码生成", businessType = BusinessType.DELETE) + @DeleteMapping("/{tableIds}") + public AjaxResult remove(@PathVariable Long[] tableIds) { + genTableService.deleteGenTableByIds(tableIds); + return success(); + } + + /** + * 预览代码 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:preview')") + @GetMapping("/preview/{tableId}") + public AjaxResult preview(@PathVariable("tableId") Long tableId) throws IOException { + Map dataMap = genTableService.previewCode(tableId); + return success(dataMap); + } + + /** + * 生成代码(下载方式) + */ + @PreAuthorize("@ss.hasPermi('tool:gen:code')") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/download/{tableName}") + public void download(HttpServletResponse response, @PathVariable("tableName") String tableName) throws IOException { + byte[] data = genTableService.downloadCode(tableName); + genCode(response, data); + } + + /** + * 生成代码(自定义路径) + */ + @PreAuthorize("@ss.hasPermi('tool:gen:code')") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/genCode/{tableName}") + public AjaxResult genCode(@PathVariable("tableName") String tableName) { + genTableService.generatorCode(tableName); + return success(); + } + + /** + * 同步数据库 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:edit')") + @Log(title = "代码生成", businessType = BusinessType.UPDATE) + @GetMapping("/synchDb/{tableName}") + public AjaxResult synchDb(@PathVariable("tableName") String tableName) { + genTableService.synchDb(tableName); + return success(); + } + + /** + * 批量生成代码 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:code')") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/batchGenCode") + public void batchGenCode(HttpServletResponse response, String tables) throws IOException { + String[] tableNames = Convert.toStrArray(tables); + byte[] data = genTableService.downloadCode(tableNames); + genCode(response, data); + } + + /** + * 生成zip文件 + */ + private void genCode(HttpServletResponse response, byte[] data) throws IOException { + response.reset(); + response.addHeader("Access-Control-Allow-Origin", "*"); + response.addHeader("Access-Control-Expose-Headers", "Content-Disposition"); + response.setHeader("Content-Disposition", "attachment; filename=\"ruoyi.zip\""); + response.addHeader("Content-Length", "" + data.length); + response.setContentType("application/octet-stream; charset=UTF-8"); + IOUtils.write(data, response.getOutputStream()); + } +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTable.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTable.java new file mode 100644 index 00000000..269779cf --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTable.java @@ -0,0 +1,372 @@ +package com.ruoyi.generator.domain; + +import java.util.List; +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; +import org.apache.commons.lang3.ArrayUtils; +import com.ruoyi.common.constant.GenConstants; +import com.ruoyi.common.core.domain.BaseEntity; +import com.ruoyi.common.utils.StringUtils; + +/** + * 业务表 gen_table + * + * @author ruoyi + */ +public class GenTable extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 编号 */ + private Long tableId; + + /** 表名称 */ + @NotBlank(message = "表名称不能为空") + private String tableName; + + /** 表描述 */ + @NotBlank(message = "表描述不能为空") + private String tableComment; + + /** 关联父表的表名 */ + private String subTableName; + + /** 本表关联父表的外键名 */ + private String subTableFkName; + + /** 实体类名称(首字母大写) */ + @NotBlank(message = "实体类名称不能为空") + private String className; + + /** 使用的模板(crud单表操作 tree树表操作 sub主子表操作) */ + private String tplCategory; + + /** 生成包路径 */ + @NotBlank(message = "生成包路径不能为空") + private String packageName; + + /** 生成模块名 */ + @NotBlank(message = "生成模块名不能为空") + private String moduleName; + + /** 生成业务名 */ + @NotBlank(message = "生成业务名不能为空") + private String businessName; + + /** 生成功能名 */ + @NotBlank(message = "生成功能名不能为空") + private String functionName; + + /** 生成作者 */ + @NotBlank(message = "作者不能为空") + private String functionAuthor; + + /** 生成代码方式(0zip压缩包 1自定义路径) */ + private String genType; + + /** 生成路径(不填默认项目路径) */ + private String genPath; + + /** 主键信息 */ + private GenTableColumn pkColumn; + + /** 子表信息 */ + private GenTable subTable; + + /** 表列信息 */ + @Valid + private List columns; + + /** 其它生成选项 */ + private String options; + + /** 树编码字段 */ + private String treeCode; + + /** 树父编码字段 */ + private String treeParentCode; + + /** 树名称字段 */ + private String treeName; + + /** 上级菜单ID字段 */ + private String parentMenuId; + + /** 上级菜单名称字段 */ + private String parentMenuName; + + public Long getTableId() + { + return tableId; + } + + public void setTableId(Long tableId) + { + this.tableId = tableId; + } + + public String getTableName() + { + return tableName; + } + + public void setTableName(String tableName) + { + this.tableName = tableName; + } + + public String getTableComment() + { + return tableComment; + } + + public void setTableComment(String tableComment) + { + this.tableComment = tableComment; + } + + public String getSubTableName() + { + return subTableName; + } + + public void setSubTableName(String subTableName) + { + this.subTableName = subTableName; + } + + public String getSubTableFkName() + { + return subTableFkName; + } + + public void setSubTableFkName(String subTableFkName) + { + this.subTableFkName = subTableFkName; + } + + public String getClassName() + { + return className; + } + + public void setClassName(String className) + { + this.className = className; + } + + public String getTplCategory() + { + return tplCategory; + } + + public void setTplCategory(String tplCategory) + { + this.tplCategory = tplCategory; + } + + public String getPackageName() + { + return packageName; + } + + public void setPackageName(String packageName) + { + this.packageName = packageName; + } + + public String getModuleName() + { + return moduleName; + } + + public void setModuleName(String moduleName) + { + this.moduleName = moduleName; + } + + public String getBusinessName() + { + return businessName; + } + + public void setBusinessName(String businessName) + { + this.businessName = businessName; + } + + public String getFunctionName() + { + return functionName; + } + + public void setFunctionName(String functionName) + { + this.functionName = functionName; + } + + public String getFunctionAuthor() + { + return functionAuthor; + } + + public void setFunctionAuthor(String functionAuthor) + { + this.functionAuthor = functionAuthor; + } + + public String getGenType() + { + return genType; + } + + public void setGenType(String genType) + { + this.genType = genType; + } + + public String getGenPath() + { + return genPath; + } + + public void setGenPath(String genPath) + { + this.genPath = genPath; + } + + public GenTableColumn getPkColumn() + { + return pkColumn; + } + + public void setPkColumn(GenTableColumn pkColumn) + { + this.pkColumn = pkColumn; + } + + public GenTable getSubTable() + { + return subTable; + } + + public void setSubTable(GenTable subTable) + { + this.subTable = subTable; + } + + public List getColumns() + { + return columns; + } + + public void setColumns(List columns) + { + this.columns = columns; + } + + public String getOptions() + { + return options; + } + + public void setOptions(String options) + { + this.options = options; + } + + public String getTreeCode() + { + return treeCode; + } + + public void setTreeCode(String treeCode) + { + this.treeCode = treeCode; + } + + public String getTreeParentCode() + { + return treeParentCode; + } + + public void setTreeParentCode(String treeParentCode) + { + this.treeParentCode = treeParentCode; + } + + public String getTreeName() + { + return treeName; + } + + public void setTreeName(String treeName) + { + this.treeName = treeName; + } + + public String getParentMenuId() + { + return parentMenuId; + } + + public void setParentMenuId(String parentMenuId) + { + this.parentMenuId = parentMenuId; + } + + public String getParentMenuName() + { + return parentMenuName; + } + + public void setParentMenuName(String parentMenuName) + { + this.parentMenuName = parentMenuName; + } + + public boolean isSub() + { + return isSub(this.tplCategory); + } + + public static boolean isSub(String tplCategory) + { + return tplCategory != null && StringUtils.equals(GenConstants.TPL_SUB, tplCategory); + } + + public boolean isTree() + { + return isTree(this.tplCategory); + } + + public static boolean isTree(String tplCategory) + { + return tplCategory != null && StringUtils.equals(GenConstants.TPL_TREE, tplCategory); + } + + public boolean isCrud() + { + return isCrud(this.tplCategory); + } + + public static boolean isCrud(String tplCategory) + { + return tplCategory != null && StringUtils.equals(GenConstants.TPL_CRUD, tplCategory); + } + + public boolean isSuperColumn(String javaField) + { + return isSuperColumn(this.tplCategory, javaField); + } + + 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); + } +} \ No newline at end of file diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java new file mode 100644 index 00000000..d1733b64 --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java @@ -0,0 +1,373 @@ +package com.ruoyi.generator.domain; + +import javax.validation.constraints.NotBlank; +import com.ruoyi.common.core.domain.BaseEntity; +import com.ruoyi.common.utils.StringUtils; + +/** + * 代码生成业务字段表 gen_table_column + * + * @author ruoyi + */ +public class GenTableColumn extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 编号 */ + private Long columnId; + + /** 归属表编号 */ + private Long tableId; + + /** 列名称 */ + private String columnName; + + /** 列描述 */ + private String columnComment; + + /** 列类型 */ + private String columnType; + + /** JAVA类型 */ + private String javaType; + + /** JAVA字段名 */ + @NotBlank(message = "Java属性不能为空") + private String javaField; + + /** 是否主键(1是) */ + private String isPk; + + /** 是否自增(1是) */ + private String isIncrement; + + /** 是否必填(1是) */ + private String isRequired; + + /** 是否为插入字段(1是) */ + private String isInsert; + + /** 是否编辑字段(1是) */ + private String isEdit; + + /** 是否列表字段(1是) */ + private String isList; + + /** 是否查询字段(1是) */ + private String isQuery; + + /** 查询方式(EQ等于、NE不等于、GT大于、LT小于、LIKE模糊、BETWEEN范围) */ + private String queryType; + + /** 显示类型(input文本框、textarea文本域、select下拉框、checkbox复选框、radio单选框、datetime日期控件、image图片上传控件、upload文件上传控件、editor富文本控件) */ + private String htmlType; + + /** 字典类型 */ + private String dictType; + + /** 排序 */ + private Integer sort; + + public void setColumnId(Long columnId) + { + this.columnId = columnId; + } + + public Long getColumnId() + { + return columnId; + } + + public void setTableId(Long tableId) + { + this.tableId = tableId; + } + + public Long getTableId() + { + return tableId; + } + + public void setColumnName(String columnName) + { + this.columnName = columnName; + } + + public String getColumnName() + { + return columnName; + } + + public void setColumnComment(String columnComment) + { + this.columnComment = columnComment; + } + + public String getColumnComment() + { + return columnComment; + } + + public void setColumnType(String columnType) + { + this.columnType = columnType; + } + + public String getColumnType() + { + return columnType; + } + + public void setJavaType(String javaType) + { + this.javaType = javaType; + } + + public String getJavaType() + { + return javaType; + } + + public void setJavaField(String javaField) + { + this.javaField = javaField; + } + + public String getJavaField() + { + return javaField; + } + + public String getCapJavaField() + { + return StringUtils.capitalize(javaField); + } + + public void setIsPk(String isPk) + { + this.isPk = isPk; + } + + public String getIsPk() + { + return isPk; + } + + public boolean isPk() + { + return isPk(this.isPk); + } + + public boolean isPk(String isPk) + { + return isPk != null && StringUtils.equals("1", isPk); + } + + public String getIsIncrement() + { + return isIncrement; + } + + public void setIsIncrement(String isIncrement) + { + this.isIncrement = isIncrement; + } + + public boolean isIncrement() + { + return isIncrement(this.isIncrement); + } + + public boolean isIncrement(String isIncrement) + { + return isIncrement != null && StringUtils.equals("1", isIncrement); + } + + public void setIsRequired(String isRequired) + { + this.isRequired = isRequired; + } + + public String getIsRequired() + { + return isRequired; + } + + public boolean isRequired() + { + return isRequired(this.isRequired); + } + + public boolean isRequired(String isRequired) + { + return isRequired != null && StringUtils.equals("1", isRequired); + } + + public void setIsInsert(String isInsert) + { + this.isInsert = isInsert; + } + + public String getIsInsert() + { + return isInsert; + } + + public boolean isInsert() + { + return isInsert(this.isInsert); + } + + public boolean isInsert(String isInsert) + { + return isInsert != null && StringUtils.equals("1", isInsert); + } + + public void setIsEdit(String isEdit) + { + this.isEdit = isEdit; + } + + public String getIsEdit() + { + return isEdit; + } + + public boolean isEdit() + { + return isInsert(this.isEdit); + } + + public boolean isEdit(String isEdit) + { + return isEdit != null && StringUtils.equals("1", isEdit); + } + + public void setIsList(String isList) + { + this.isList = isList; + } + + public String getIsList() + { + return isList; + } + + public boolean isList() + { + return isList(this.isList); + } + + public boolean isList(String isList) + { + return isList != null && StringUtils.equals("1", isList); + } + + public void setIsQuery(String isQuery) + { + this.isQuery = isQuery; + } + + public String getIsQuery() + { + return isQuery; + } + + public boolean isQuery() + { + return isQuery(this.isQuery); + } + + public boolean isQuery(String isQuery) + { + return isQuery != null && StringUtils.equals("1", isQuery); + } + + public void setQueryType(String queryType) + { + this.queryType = queryType; + } + + public String getQueryType() + { + return queryType; + } + + public String getHtmlType() + { + return htmlType; + } + + public void setHtmlType(String htmlType) + { + this.htmlType = htmlType; + } + + public void setDictType(String dictType) + { + this.dictType = dictType; + } + + public String getDictType() + { + return dictType; + } + + public void setSort(Integer sort) + { + this.sort = sort; + } + + public Integer getSort() + { + return sort; + } + + public boolean isSuperColumn() + { + return isSuperColumn(this.javaField); + } + + public static boolean isSuperColumn(String javaField) + { + return StringUtils.equalsAnyIgnoreCase(javaField, + // BaseEntity + "createBy", "createTime", "updateBy", "updateTime", "remark", + // TreeEntity + "parentName", "parentId", "orderNum", "ancestors"); + } + + public boolean isUsableColumn() + { + return isUsableColumn(javaField); + } + + public static boolean isUsableColumn(String javaField) + { + // isSuperColumn()中的名单用于避免生成多余Domain属性,若某些属性在生成页面时需要用到不能忽略,则放在此处白名单 + return StringUtils.equalsAnyIgnoreCase(javaField, "parentId", "orderNum", "remark"); + } + + public String readConverterExp() + { + String remarks = StringUtils.substringBetween(this.columnComment, "(", ")"); + StringBuffer sb = new StringBuffer(); + if (StringUtils.isNotEmpty(remarks)) + { + for (String value : remarks.split(" ")) + { + if (StringUtils.isNotEmpty(value)) + { + Object startStr = value.subSequence(0, 1); + String endStr = value.substring(1); + sb.append("").append(startStr).append("=").append(endStr).append(","); + } + } + return sb.deleteCharAt(sb.length() - 1).toString(); + } + else + { + return this.columnComment; + } + } +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableColumnMapper.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableColumnMapper.java new file mode 100644 index 00000000..951e1667 --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableColumnMapper.java @@ -0,0 +1,60 @@ +package com.ruoyi.generator.mapper; + +import java.util.List; +import com.ruoyi.generator.domain.GenTableColumn; + +/** + * 业务字段 数据层 + * + * @author ruoyi + */ +public interface GenTableColumnMapper +{ + /** + * 根据表名称查询列信息 + * + * @param tableName 表名称 + * @return 列信息 + */ + public List selectDbTableColumnsByName(String tableName); + + /** + * 查询业务字段列表 + * + * @param tableId 业务字段编号 + * @return 业务字段集合 + */ + public List selectGenTableColumnListByTableId(Long tableId); + + /** + * 新增业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + public int insertGenTableColumn(GenTableColumn genTableColumn); + + /** + * 修改业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + public int updateGenTableColumn(GenTableColumn genTableColumn); + + /** + * 删除业务字段 + * + * @param genTableColumns 列数据 + * @return 结果 + */ + public int deleteGenTableColumns(List genTableColumns); + + /** + * 批量删除业务字段 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteGenTableColumnByIds(Long[] ids); +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableMapper.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableMapper.java new file mode 100644 index 00000000..9b330df8 --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableMapper.java @@ -0,0 +1,83 @@ +package com.ruoyi.generator.mapper; + +import java.util.List; +import com.ruoyi.generator.domain.GenTable; + +/** + * 业务 数据层 + * + * @author ruoyi + */ +public interface GenTableMapper +{ + /** + * 查询业务列表 + * + * @param genTable 业务信息 + * @return 业务集合 + */ + public List selectGenTableList(GenTable genTable); + + /** + * 查询据库列表 + * + * @param genTable 业务信息 + * @return 数据库表集合 + */ + public List selectDbTableList(GenTable genTable); + + /** + * 查询据库列表 + * + * @param tableNames 表名称组 + * @return 数据库表集合 + */ + public List selectDbTableListByNames(String[] tableNames); + + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + public List selectGenTableAll(); + + /** + * 查询表ID业务信息 + * + * @param id 业务ID + * @return 业务信息 + */ + public GenTable selectGenTableById(Long id); + + /** + * 查询表名称业务信息 + * + * @param tableName 表名称 + * @return 业务信息 + */ + public GenTable selectGenTableByName(String tableName); + + /** + * 新增业务 + * + * @param genTable 业务信息 + * @return 结果 + */ + public int insertGenTable(GenTable genTable); + + /** + * 修改业务 + * + * @param genTable 业务信息 + * @return 结果 + */ + public int updateGenTable(GenTable genTable); + + /** + * 批量删除业务 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteGenTableByIds(Long[] ids); +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableColumnServiceImpl.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableColumnServiceImpl.java new file mode 100644 index 00000000..0679689d --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableColumnServiceImpl.java @@ -0,0 +1,68 @@ +package com.ruoyi.generator.service; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.generator.domain.GenTableColumn; +import com.ruoyi.generator.mapper.GenTableColumnMapper; + +/** + * 业务字段 服务层实现 + * + * @author ruoyi + */ +@Service +public class GenTableColumnServiceImpl implements IGenTableColumnService +{ + @Autowired + private GenTableColumnMapper genTableColumnMapper; + + /** + * 查询业务字段列表 + * + * @param tableId 业务字段编号 + * @return 业务字段集合 + */ + @Override + public List selectGenTableColumnListByTableId(Long tableId) + { + return genTableColumnMapper.selectGenTableColumnListByTableId(tableId); + } + + /** + * 新增业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + @Override + public int insertGenTableColumn(GenTableColumn genTableColumn) + { + return genTableColumnMapper.insertGenTableColumn(genTableColumn); + } + + /** + * 修改业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + @Override + public int updateGenTableColumn(GenTableColumn genTableColumn) + { + return genTableColumnMapper.updateGenTableColumn(genTableColumn); + } + + /** + * 删除业务字段对象 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + @Override + public int deleteGenTableColumnByIds(String ids) + { + return genTableColumnMapper.deleteGenTableColumnByIds(Convert.toLongArray(ids)); + } +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java new file mode 100644 index 00000000..f4b12e9c --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java @@ -0,0 +1,498 @@ +package com.ruoyi.generator.service; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.baomidou.mybatisplus.generator.config.po.TableField; +import com.baomidou.mybatisplus.generator.config.po.TableInfo; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.GenConstants; +import com.ruoyi.common.core.text.CharsetKit; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.collection.CollectionUtils; +import com.ruoyi.generator.domain.GenTable; +import com.ruoyi.generator.domain.GenTableColumn; +import com.ruoyi.generator.mapper.GenTableColumnMapper; +import com.ruoyi.generator.mapper.GenTableMapper; +import com.ruoyi.generator.util.GenTableUtils; +import com.ruoyi.generator.util.GenUtils; +import com.ruoyi.generator.util.VelocityInitializer; +import com.ruoyi.generator.util.VelocityUtils; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.velocity.Template; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.Velocity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.StringWriter; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +/** + * 业务 服务层实现 + * + * @author ruoyi + */ +@Service +public class GenTableServiceImpl implements IGenTableService { + private static final Logger log = LoggerFactory.getLogger(GenTableServiceImpl.class); + + @Autowired + private GenTableMapper genTableMapper; + + @Autowired + private GenTableColumnMapper genTableColumnMapper; + + /** + * 查询业务信息 + * + * @param id 业务ID + * @return 业务信息 + */ + @Override + public GenTable selectGenTableById(Long id) { + GenTable genTable = genTableMapper.selectGenTableById(id); + setTableFromOptions(genTable); + return genTable; + } + + /** + * 查询业务列表 + * + * @param genTable 业务信息 + * @return 业务集合 + */ + @Override + public List selectGenTableList(GenTable genTable) { + return genTableMapper.selectGenTableList(genTable); + } + + /** + * 查询据库列表 + * + * @param genTable 业务信息 + * @return 数据库表集合 + */ + @Override + public List selectDbTableList(GenTable genTable) { + List tableList = GenTableUtils.getTableList(genTable.getTableName(), genTable.getTableComment()); + // 移除置顶前缀的表名 + tableList.removeIf(table -> table.getName().toUpperCase().startsWith("QRTZ_")); + tableList.removeIf(table -> table.getName().toUpperCase().startsWith("ACT_")); + tableList.removeIf(table -> table.getName().toUpperCase().startsWith("FLW_")); + // 移除已经生成的表 + // 移除在 Codegen 中,已经存在的 + List genTables = genTableMapper.selectGenTableList(new GenTable()); + Set existsTables = CollectionUtils.convertSet(genTables, GenTable::getTableName); + tableList.removeIf(table -> existsTables.contains(table.getName())); + List result = tableList.stream().map(tmp -> { + GenTable data = new GenTable(); + data.setTableName(tmp.getName()); + data.setTableComment(tmp.getComment()); + return data; + }).collect(Collectors.toList()); + return result; + //return genTableMapper.selectDbTableList(genTable); + } + + /** + * 查询据库列表 + * + * @param tableNames 表名称组 + * @return 数据库表集合 + */ + @Override + public List selectDbTableListByNames(String[] tableNames) { + List result = new ArrayList<>(tableNames.length); + for (String tableName : tableNames) { + TableInfo table = GenTableUtils.getTable(tableName); + GenTable data = new GenTable(); + data.setTableName(table.getName()); + data.setTableComment(table.getComment()); + result.add(data); + } + return result; + //return genTableMapper.selectDbTableListByNames(tableNames); + } + + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + @Override + public List selectGenTableAll() { + return genTableMapper.selectGenTableAll(); + } + + /** + * 修改业务 + * + * @param genTable 业务信息 + * @return 结果 + */ + @Override + @Transactional + public void updateGenTable(GenTable genTable) { + String options = JSON.toJSONString(genTable.getParams()); + genTable.setOptions(options); + int row = genTableMapper.updateGenTable(genTable); + if (row > 0) { + for (GenTableColumn cenTableColumn : genTable.getColumns()) { + genTableColumnMapper.updateGenTableColumn(cenTableColumn); + } + } + } + + /** + * 删除业务对象 + * + * @param tableIds 需要删除的数据ID + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteGenTableByIds(Long[] tableIds) { + genTableMapper.deleteGenTableByIds(tableIds); + genTableColumnMapper.deleteGenTableColumnByIds(tableIds); + } + + /** + * 导入表结构 + * + * @param tableList 导入表列表 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void importGenTable(List tableList) { + String operName = SecurityUtils.getUsername(); + try { + for (GenTable table : tableList) { + String tableName = table.getTableName(); + GenUtils.initTable(table, operName); + int row = genTableMapper.insertGenTable(table); + if (row > 0) { + // 保存列信息 + TableInfo tableInfo = GenTableUtils.getTable(tableName); + List fields = tableInfo.getFields(); + AtomicInteger index = new AtomicInteger(1); + List genTableColumns = fields.stream().map(tmp -> { + GenTableColumn data = new GenTableColumn(); + data.setColumnName(tmp.getColumnName()); + data.setColumnComment(tmp.getComment()); + data.setIsRequired(tmp.getMetaInfo().isNullable() ? "1" : "0"); + data.setIsPk(tmp.isKeyFlag() ? "1" : "0"); + data.setSort(index.getAndIncrement()); + data.setIsIncrement(tmp.isKeyIdentityFlag() ? "1" : "0"); + data.setColumnType(tmp.getType()); + return data; + }).collect(Collectors.toList()); + //List genTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName); + for (GenTableColumn column : genTableColumns) { + GenUtils.initColumnField(column, table); + genTableColumnMapper.insertGenTableColumn(column); + } + } + } + } catch (Exception e) { + throw new ServiceException("导入失败:" + e.getMessage()); + } + } + + /** + * 预览代码 + * + * @param tableId 表编号 + * @return 预览数据列表 + */ + @Override + public Map previewCode(Long tableId) { + Map dataMap = new LinkedHashMap<>(); + // 查询表信息 + GenTable table = genTableMapper.selectGenTableById(tableId); + // 设置主子表信息 + setSubTable(table); + // 设置主键列信息 + setPkColumn(table); + VelocityInitializer.initVelocity(); + + VelocityContext context = VelocityUtils.prepareContext(table); + + // 获取模板列表 + List templates = VelocityUtils.getTemplateList(table.getTplCategory()); + for (String template : templates) { + // 渲染模板 + StringWriter sw = new StringWriter(); + Template tpl = Velocity.getTemplate(template, Constants.UTF8); + tpl.merge(context, sw); + dataMap.put(template, sw.toString()); + } + return dataMap; + } + + /** + * 生成代码(下载方式) + * + * @param tableName 表名称 + * @return 数据 + */ + @Override + public byte[] downloadCode(String tableName) { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ZipOutputStream zip = new ZipOutputStream(outputStream); + generatorCode(tableName, zip); + IOUtils.closeQuietly(zip); + return outputStream.toByteArray(); + } + + /** + * 生成代码(自定义路径) + * + * @param tableName 表名称 + */ + @Override + public void generatorCode(String tableName) { + // 查询表信息 + GenTable table = genTableMapper.selectGenTableByName(tableName); + // 设置主子表信息 + setSubTable(table); + // 设置主键列信息 + setPkColumn(table); + + VelocityInitializer.initVelocity(); + + VelocityContext context = VelocityUtils.prepareContext(table); + + // 获取模板列表 + List templates = VelocityUtils.getTemplateList(table.getTplCategory()); + for (String template : templates) { + if (!StringUtils.containsAny(template, "sql.vm", "api.js.vm", "index.vue.vm", "index-tree.vue.vm")) { + // 渲染模板 + StringWriter sw = new StringWriter(); + Template tpl = Velocity.getTemplate(template, Constants.UTF8); + tpl.merge(context, sw); + try { + String path = getGenPath(table, template); + FileUtils.writeStringToFile(new File(path), sw.toString(), CharsetKit.UTF_8); + } catch (IOException e) { + throw new ServiceException("渲染模板失败,表名:" + table.getTableName()); + } + } + } + } + + /** + * 同步数据库 + * + * @param tableName 表名称 + */ + @Override + @Transactional + public void synchDb(String tableName) { + GenTable table = genTableMapper.selectGenTableByName(tableName); + List tableColumns = table.getColumns(); + Map tableColumnMap = tableColumns.stream().collect(Collectors.toMap(GenTableColumn::getColumnName, Function.identity())); + + List dbTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName); + if (StringUtils.isEmpty(dbTableColumns)) { + throw new ServiceException("同步数据失败,原表结构不存在"); + } + List dbTableColumnNames = dbTableColumns.stream().map(GenTableColumn::getColumnName).collect(Collectors.toList()); + + dbTableColumns.forEach(column -> { + GenUtils.initColumnField(column, table); + if (tableColumnMap.containsKey(column.getColumnName())) { + GenTableColumn prevColumn = tableColumnMap.get(column.getColumnName()); + column.setColumnId(prevColumn.getColumnId()); + if (column.isList()) { + // 如果是列表,继续保留查询方式/字典类型选项 + column.setDictType(prevColumn.getDictType()); + column.setQueryType(prevColumn.getQueryType()); + } + if (StringUtils.isNotEmpty(prevColumn.getIsRequired()) && !column.isPk() + && (column.isInsert() || column.isEdit()) + && ((column.isUsableColumn()) || (!column.isSuperColumn()))) { + // 如果是(新增/修改&非主键/非忽略及父属性),继续保留必填/显示类型选项 + column.setIsRequired(prevColumn.getIsRequired()); + column.setHtmlType(prevColumn.getHtmlType()); + } + genTableColumnMapper.updateGenTableColumn(column); + } else { + genTableColumnMapper.insertGenTableColumn(column); + } + }); + + List delColumns = tableColumns.stream().filter(column -> !dbTableColumnNames.contains(column.getColumnName())).collect(Collectors.toList()); + if (StringUtils.isNotEmpty(delColumns)) { + genTableColumnMapper.deleteGenTableColumns(delColumns); + } + } + + /** + * 批量生成代码(下载方式) + * + * @param tableNames 表数组 + * @return 数据 + */ + @Override + public byte[] downloadCode(String[] tableNames) { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ZipOutputStream zip = new ZipOutputStream(outputStream); + for (String tableName : tableNames) { + generatorCode(tableName, zip); + } + IOUtils.closeQuietly(zip); + return outputStream.toByteArray(); + } + + /** + * 查询表信息并生成代码 + */ + private void generatorCode(String tableName, ZipOutputStream zip) { + // 查询表信息 + GenTable table = genTableMapper.selectGenTableByName(tableName); + // 设置主子表信息 + setSubTable(table); + // 设置主键列信息 + setPkColumn(table); + + VelocityInitializer.initVelocity(); + + VelocityContext context = VelocityUtils.prepareContext(table); + + // 获取模板列表 + List templates = VelocityUtils.getTemplateList(table.getTplCategory()); + for (String template : templates) { + // 渲染模板 + StringWriter sw = new StringWriter(); + Template tpl = Velocity.getTemplate(template, Constants.UTF8); + tpl.merge(context, sw); + try { + // 添加到zip + zip.putNextEntry(new ZipEntry(VelocityUtils.getFileName(template, table))); + IOUtils.write(sw.toString(), zip, Constants.UTF8); + IOUtils.closeQuietly(sw); + zip.flush(); + zip.closeEntry(); + } catch (IOException e) { + log.error("渲染模板失败,表名:" + table.getTableName(), e); + } + } + } + + /** + * 修改保存参数校验 + * + * @param genTable 业务信息 + */ + @Override + public void validateEdit(GenTable genTable) { + if (GenConstants.TPL_TREE.equals(genTable.getTplCategory())) { + String options = JSON.toJSONString(genTable.getParams()); + JSONObject paramsObj = JSON.parseObject(options); + if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_CODE))) { + throw new ServiceException("树编码字段不能为空"); + } else if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_PARENT_CODE))) { + throw new ServiceException("树父编码字段不能为空"); + } else if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_NAME))) { + throw new ServiceException("树名称字段不能为空"); + } else if (GenConstants.TPL_SUB.equals(genTable.getTplCategory())) { + if (StringUtils.isEmpty(genTable.getSubTableName())) { + throw new ServiceException("关联子表的表名不能为空"); + } else if (StringUtils.isEmpty(genTable.getSubTableFkName())) { + throw new ServiceException("子表关联的外键名不能为空"); + } + } + } + } + + /** + * 设置主键列信息 + * + * @param table 业务表信息 + */ + public void setPkColumn(GenTable table) { + for (GenTableColumn column : table.getColumns()) { + if (column.isPk()) { + table.setPkColumn(column); + break; + } + } + if (StringUtils.isNull(table.getPkColumn())) { + table.setPkColumn(table.getColumns().get(0)); + } + if (GenConstants.TPL_SUB.equals(table.getTplCategory())) { + for (GenTableColumn column : table.getSubTable().getColumns()) { + if (column.isPk()) { + table.getSubTable().setPkColumn(column); + break; + } + } + if (StringUtils.isNull(table.getSubTable().getPkColumn())) { + table.getSubTable().setPkColumn(table.getSubTable().getColumns().get(0)); + } + } + } + + /** + * 设置主子表信息 + * + * @param table 业务表信息 + */ + public void setSubTable(GenTable table) { + String subTableName = table.getSubTableName(); + if (StringUtils.isNotEmpty(subTableName)) { + table.setSubTable(genTableMapper.selectGenTableByName(subTableName)); + } + } + + /** + * 设置代码生成其他选项值 + * + * @param genTable 设置后的生成对象 + */ + public void setTableFromOptions(GenTable genTable) { + JSONObject paramsObj = JSON.parseObject(genTable.getOptions()); + if (StringUtils.isNotNull(paramsObj)) { + String treeCode = paramsObj.getString(GenConstants.TREE_CODE); + String treeParentCode = paramsObj.getString(GenConstants.TREE_PARENT_CODE); + String treeName = paramsObj.getString(GenConstants.TREE_NAME); + String parentMenuId = paramsObj.getString(GenConstants.PARENT_MENU_ID); + String parentMenuName = paramsObj.getString(GenConstants.PARENT_MENU_NAME); + + genTable.setTreeCode(treeCode); + genTable.setTreeParentCode(treeParentCode); + genTable.setTreeName(treeName); + genTable.setParentMenuId(parentMenuId); + genTable.setParentMenuName(parentMenuName); + } + } + + /** + * 获取代码生成地址 + * + * @param table 业务表信息 + * @param template 模板文件路径 + * @return 生成地址 + */ + public static String getGenPath(GenTable table, String template) { + String genPath = table.getGenPath(); + if (StringUtils.equals(genPath, "/")) { + return System.getProperty("user.dir") + File.separator + "src" + File.separator + VelocityUtils.getFileName(template, table); + } + return genPath + File.separator + VelocityUtils.getFileName(template, table); + } +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableColumnService.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableColumnService.java new file mode 100644 index 00000000..3037f707 --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableColumnService.java @@ -0,0 +1,44 @@ +package com.ruoyi.generator.service; + +import java.util.List; +import com.ruoyi.generator.domain.GenTableColumn; + +/** + * 业务字段 服务层 + * + * @author ruoyi + */ +public interface IGenTableColumnService +{ + /** + * 查询业务字段列表 + * + * @param tableId 业务字段编号 + * @return 业务字段集合 + */ + public List selectGenTableColumnListByTableId(Long tableId); + + /** + * 新增业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + public int insertGenTableColumn(GenTableColumn genTableColumn); + + /** + * 修改业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + public int updateGenTableColumn(GenTableColumn genTableColumn); + + /** + * 删除业务字段信息 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteGenTableColumnByIds(String ids); +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java new file mode 100644 index 00000000..9d53f95f --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java @@ -0,0 +1,121 @@ +package com.ruoyi.generator.service; + +import java.util.List; +import java.util.Map; +import com.ruoyi.generator.domain.GenTable; + +/** + * 业务 服务层 + * + * @author ruoyi + */ +public interface IGenTableService +{ + /** + * 查询业务列表 + * + * @param genTable 业务信息 + * @return 业务集合 + */ + public List selectGenTableList(GenTable genTable); + + /** + * 查询据库列表 + * + * @param genTable 业务信息 + * @return 数据库表集合 + */ + public List selectDbTableList(GenTable genTable); + + /** + * 查询据库列表 + * + * @param tableNames 表名称组 + * @return 数据库表集合 + */ + public List selectDbTableListByNames(String[] tableNames); + + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + public List selectGenTableAll(); + + /** + * 查询业务信息 + * + * @param id 业务ID + * @return 业务信息 + */ + public GenTable selectGenTableById(Long id); + + /** + * 修改业务 + * + * @param genTable 业务信息 + * @return 结果 + */ + public void updateGenTable(GenTable genTable); + + /** + * 删除业务信息 + * + * @param tableIds 需要删除的表数据ID + * @return 结果 + */ + public void deleteGenTableByIds(Long[] tableIds); + + /** + * 导入表结构 + * + * @param tableList 导入表列表 + */ + public void importGenTable(List tableList); + + /** + * 预览代码 + * + * @param tableId 表编号 + * @return 预览数据列表 + */ + public Map previewCode(Long tableId); + + /** + * 生成代码(下载方式) + * + * @param tableName 表名称 + * @return 数据 + */ + public byte[] downloadCode(String tableName); + + /** + * 生成代码(自定义路径) + * + * @param tableName 表名称 + * @return 数据 + */ + public void generatorCode(String tableName); + + /** + * 同步数据库 + * + * @param tableName 表名称 + */ + public void synchDb(String tableName); + + /** + * 批量生成代码(下载方式) + * + * @param tableNames 表数组 + * @return 数据 + */ + public byte[] downloadCode(String[] tableNames); + + /** + * 修改保存参数校验 + * + * @param genTable 业务信息 + */ + public void validateEdit(GenTable genTable); +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenTableUtils.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenTableUtils.java new file mode 100644 index 00000000..a6a8609a --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenTableUtils.java @@ -0,0 +1,56 @@ +package com.ruoyi.generator.util; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.generator.config.DataSourceConfig; +import com.baomidou.mybatisplus.generator.config.GlobalConfig; +import com.baomidou.mybatisplus.generator.config.StrategyConfig; +import com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder; +import com.baomidou.mybatisplus.generator.config.po.TableInfo; +import com.baomidou.mybatisplus.generator.config.rules.DateType; +import com.ruoyi.common.utils.spring.SpringUtils; +import org.springframework.core.env.Environment; + +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; + +public class GenTableUtils { + + private static final String DATASOURCE_DYNAMIC_KEY = "spring.datasource.druid.master"; + + public static Environment environment = SpringUtils.getBean(Environment.class); + + public static List getTableList(String nameLike, String commentLike) { + List tables = getTableList0(null); + return tables.stream().filter(tableInfo -> (StrUtil.isEmpty(nameLike) || tableInfo.getName().contains(nameLike)) + && (StrUtil.isEmpty(commentLike) || tableInfo.getComment().contains(commentLike))) + .collect(Collectors.toList()); + } + + public static TableInfo getTable(String name) { + return CollUtil.getFirst(getTableList0(name)); + } + + public static List getTableList0(String name) { + String url = environment.getProperty(DATASOURCE_DYNAMIC_KEY + ".url"); + String username = environment.getProperty(DATASOURCE_DYNAMIC_KEY + ".username"); + String password = environment.getProperty(DATASOURCE_DYNAMIC_KEY + ".password"); + // 使用 MyBatis Plus Generator 解析表结构 + DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder(url, username, + password).build(); + StrategyConfig.Builder strategyConfig = new StrategyConfig.Builder(); + if (StrUtil.isNotEmpty(name)) { + strategyConfig.addInclude(name); + } + // 只使用 Date 类型,不使用 LocalDate + GlobalConfig globalConfig = new GlobalConfig.Builder().dateType(DateType.ONLY_DATE).build(); + ConfigBuilder builder = new ConfigBuilder(null, dataSourceConfig, strategyConfig.build(), + null, globalConfig, null); + // 按照名字排序 + List tables = builder.getTableInfoList(); + tables.sort(Comparator.comparing(TableInfo::getName)); + return tables; + } + +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java new file mode 100644 index 00000000..4f281621 --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java @@ -0,0 +1,257 @@ +package com.ruoyi.generator.util; + +import java.util.Arrays; +import org.apache.commons.lang3.RegExUtils; +import com.ruoyi.common.constant.GenConstants; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.generator.config.GenConfig; +import com.ruoyi.generator.domain.GenTable; +import com.ruoyi.generator.domain.GenTableColumn; + +/** + * 代码生成器 工具类 + * + * @author ruoyi + */ +public class GenUtils +{ + /** + * 初始化表信息 + */ + public static void initTable(GenTable genTable, String operName) + { + genTable.setClassName(convertClassName(genTable.getTableName())); + genTable.setPackageName(GenConfig.getPackageName()); + genTable.setModuleName(getModuleName(GenConfig.getPackageName())); + genTable.setBusinessName(getBusinessName(genTable.getTableName())); + genTable.setFunctionName(replaceText(genTable.getTableComment())); + genTable.setFunctionAuthor(GenConfig.getAuthor()); + genTable.setCreateBy(operName); + } + + /** + * 初始化列属性字段 + */ + public static void initColumnField(GenTableColumn column, GenTable table) + { + String dataType = getDbType(column.getColumnType()); + String columnName = column.getColumnName(); + column.setTableId(table.getTableId()); + column.setCreateBy(table.getCreateBy()); + // 设置java字段名 + column.setJavaField(StringUtils.toCamelCase(columnName)); + // 设置默认类型 + column.setJavaType(GenConstants.TYPE_STRING); + column.setQueryType(GenConstants.QUERY_EQ); + + if (arraysContains(GenConstants.COLUMNTYPE_STR, dataType) || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType)) + { + // 字符串长度超过500设置为文本域 + Integer columnLength = getColumnLength(column.getColumnType()); + String htmlType = columnLength >= 500 || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType) ? GenConstants.HTML_TEXTAREA : GenConstants.HTML_INPUT; + column.setHtmlType(htmlType); + } + else if (arraysContains(GenConstants.COLUMNTYPE_TIME, dataType)) + { + column.setJavaType(GenConstants.TYPE_DATE); + column.setHtmlType(GenConstants.HTML_DATETIME); + } + else if (arraysContains(GenConstants.COLUMNTYPE_NUMBER, dataType)) + { + column.setHtmlType(GenConstants.HTML_INPUT); + + // 如果是浮点型 统一用BigDecimal + String[] str = StringUtils.split(StringUtils.substringBetween(column.getColumnType(), "(", ")"), ","); + if (str != null && str.length == 2 && Integer.parseInt(str[1]) > 0) + { + column.setJavaType(GenConstants.TYPE_BIGDECIMAL); + } + // 如果是整形 + else if (str != null && str.length == 1 && Integer.parseInt(str[0]) <= 10) + { + column.setJavaType(GenConstants.TYPE_INTEGER); + } + // 长整形 + else + { + column.setJavaType(GenConstants.TYPE_LONG); + } + } + + // 插入字段(默认所有字段都需要插入) + column.setIsInsert(GenConstants.REQUIRE); + + // 编辑字段 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_EDIT, columnName) && !column.isPk()) + { + column.setIsEdit(GenConstants.REQUIRE); + } + // 列表字段 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_LIST, columnName) && !column.isPk()) + { + column.setIsList(GenConstants.REQUIRE); + } + // 查询字段 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_QUERY, columnName) && !column.isPk()) + { + column.setIsQuery(GenConstants.REQUIRE); + } + + // 查询字段类型 + if (StringUtils.endsWithIgnoreCase(columnName, "name")) + { + column.setQueryType(GenConstants.QUERY_LIKE); + } + // 状态字段设置单选框 + if (StringUtils.endsWithIgnoreCase(columnName, "status")) + { + column.setHtmlType(GenConstants.HTML_RADIO); + } + // 类型&性别字段设置下拉框 + else if (StringUtils.endsWithIgnoreCase(columnName, "type") + || StringUtils.endsWithIgnoreCase(columnName, "sex")) + { + column.setHtmlType(GenConstants.HTML_SELECT); + } + // 图片字段设置图片上传控件 + else if (StringUtils.endsWithIgnoreCase(columnName, "image")) + { + column.setHtmlType(GenConstants.HTML_IMAGE_UPLOAD); + } + // 文件字段设置文件上传控件 + else if (StringUtils.endsWithIgnoreCase(columnName, "file")) + { + column.setHtmlType(GenConstants.HTML_FILE_UPLOAD); + } + // 内容字段设置富文本控件 + else if (StringUtils.endsWithIgnoreCase(columnName, "content")) + { + column.setHtmlType(GenConstants.HTML_EDITOR); + } + } + + /** + * 校验数组是否包含指定值 + * + * @param arr 数组 + * @param targetValue 值 + * @return 是否包含 + */ + public static boolean arraysContains(String[] arr, String targetValue) + { + return Arrays.asList(arr).contains(targetValue); + } + + /** + * 获取模块名 + * + * @param packageName 包名 + * @return 模块名 + */ + public static String getModuleName(String packageName) + { + int lastIndex = packageName.lastIndexOf("."); + int nameLength = packageName.length(); + return StringUtils.substring(packageName, lastIndex + 1, nameLength); + } + + /** + * 获取业务名 + * + * @param tableName 表名 + * @return 业务名 + */ + public static String getBusinessName(String tableName) + { + int lastIndex = tableName.lastIndexOf("_"); + int nameLength = tableName.length(); + return StringUtils.substring(tableName, lastIndex + 1, nameLength); + } + + /** + * 表名转换成Java类名 + * + * @param tableName 表名称 + * @return 类名 + */ + public static String convertClassName(String tableName) + { + boolean autoRemovePre = GenConfig.getAutoRemovePre(); + String tablePrefix = GenConfig.getTablePrefix(); + if (autoRemovePre && StringUtils.isNotEmpty(tablePrefix)) + { + String[] searchList = StringUtils.split(tablePrefix, ","); + tableName = replaceFirst(tableName, searchList); + } + return StringUtils.convertToCamelCase(tableName); + } + + /** + * 批量替换前缀 + * + * @param replacementm 替换值 + * @param searchList 替换列表 + * @return + */ + public static String replaceFirst(String replacementm, String[] searchList) + { + String text = replacementm; + for (String searchString : searchList) + { + if (replacementm.startsWith(searchString)) + { + text = replacementm.replaceFirst(searchString, ""); + break; + } + } + return text; + } + + /** + * 关键字替换 + * + * @param text 需要被替换的名字 + * @return 替换后的名字 + */ + public static String replaceText(String text) + { + return RegExUtils.replaceAll(text, "(?:表|泽火)", ""); + } + + /** + * 获取数据库类型字段 + * + * @param columnType 列类型 + * @return 截取后的列类型 + */ + public static String getDbType(String columnType) + { + if (StringUtils.indexOf(columnType, "(") > 0) + { + return StringUtils.substringBefore(columnType, "("); + } + else + { + return columnType; + } + } + + /** + * 获取字段长度 + * + * @param columnType 列类型 + * @return 截取后的列类型 + */ + public static Integer getColumnLength(String columnType) + { + if (StringUtils.indexOf(columnType, "(") > 0) + { + String length = StringUtils.substringBetween(columnType, "(", ")"); + return Integer.valueOf(length); + } + else + { + return 0; + } + } +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityInitializer.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityInitializer.java new file mode 100644 index 00000000..9f694038 --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityInitializer.java @@ -0,0 +1,34 @@ +package com.ruoyi.generator.util; + +import java.util.Properties; +import org.apache.velocity.app.Velocity; +import com.ruoyi.common.constant.Constants; + +/** + * VelocityEngine工厂 + * + * @author ruoyi + */ +public class VelocityInitializer +{ + /** + * 初始化vm方法 + */ + public static void initVelocity() + { + Properties p = new Properties(); + try + { + // 加载classpath目录下的vm文件 + p.setProperty("resource.loader.file.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); + // 定义字符集 + p.setProperty(Velocity.INPUT_ENCODING, Constants.UTF8); + // 初始化Velocity引擎,指定配置Properties + Velocity.init(p); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java new file mode 100644 index 00000000..a00aa0d6 --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java @@ -0,0 +1,405 @@ +package com.ruoyi.generator.util; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.apache.velocity.VelocityContext; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.common.constant.GenConstants; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.generator.domain.GenTable; +import com.ruoyi.generator.domain.GenTableColumn; + +/** + * 模板处理工具类 + * + * @author ruoyi + */ +public class VelocityUtils +{ + /** 项目空间路径 */ + private static final String PROJECT_PATH = "main/java"; + + /** mybatis空间路径 */ + private static final String MYBATIS_PATH = "main/resources/mapper"; + + /** 默认上级菜单,系统工具 */ + private static final String DEFAULT_PARENT_MENU_ID = "3"; + + /** 模板切换 mybatis:vm;mybatis-plus:mybatisplusvm */ + private static final String VM_PATH = "mybatisplusvm"; + + /** + * 设置模板变量信息 + * + * @return 模板列表 + */ + public static VelocityContext prepareContext(GenTable genTable) + { + String moduleName = genTable.getModuleName(); + String businessName = genTable.getBusinessName(); + String packageName = genTable.getPackageName(); + String tplCategory = genTable.getTplCategory(); + String functionName = genTable.getFunctionName(); + + VelocityContext velocityContext = new VelocityContext(); + velocityContext.put("tplCategory", genTable.getTplCategory()); + velocityContext.put("tableName", genTable.getTableName()); + velocityContext.put("functionName", StringUtils.isNotEmpty(functionName) ? functionName : "【请填写功能名称】"); + velocityContext.put("ClassName", genTable.getClassName()); + velocityContext.put("className", StringUtils.uncapitalize(genTable.getClassName())); + velocityContext.put("moduleName", genTable.getModuleName()); + velocityContext.put("BusinessName", StringUtils.capitalize(genTable.getBusinessName())); + velocityContext.put("businessName", genTable.getBusinessName()); + velocityContext.put("basePackage", getPackagePrefix(packageName)); + velocityContext.put("packageName", packageName); + velocityContext.put("author", genTable.getFunctionAuthor()); + velocityContext.put("datetime", DateUtils.getDate()); + velocityContext.put("pkColumn", genTable.getPkColumn()); + velocityContext.put("importList", getImportList(genTable)); + velocityContext.put("permissionPrefix", getPermissionPrefix(moduleName, businessName)); + velocityContext.put("columns", genTable.getColumns()); + velocityContext.put("table", genTable); + velocityContext.put("dicts", getDicts(genTable)); + setMenuVelocityContext(velocityContext, genTable); + if (GenConstants.TPL_TREE.equals(tplCategory)) + { + setTreeVelocityContext(velocityContext, genTable); + } + if (GenConstants.TPL_SUB.equals(tplCategory)) + { + setSubVelocityContext(velocityContext, genTable); + } + return velocityContext; + } + + public static void setMenuVelocityContext(VelocityContext context, GenTable genTable) + { + String options = genTable.getOptions(); + JSONObject paramsObj = JSON.parseObject(options); + String parentMenuId = getParentMenuId(paramsObj); + context.put("parentMenuId", parentMenuId); + } + + public static void setTreeVelocityContext(VelocityContext context, GenTable genTable) + { + String options = genTable.getOptions(); + JSONObject paramsObj = JSON.parseObject(options); + String treeCode = getTreecode(paramsObj); + String treeParentCode = getTreeParentCode(paramsObj); + String treeName = getTreeName(paramsObj); + + context.put("treeCode", treeCode); + context.put("treeParentCode", treeParentCode); + context.put("treeName", treeName); + context.put("expandColumn", getExpandColumn(genTable)); + if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) + { + context.put("tree_parent_code", paramsObj.getString(GenConstants.TREE_PARENT_CODE)); + } + if (paramsObj.containsKey(GenConstants.TREE_NAME)) + { + context.put("tree_name", paramsObj.getString(GenConstants.TREE_NAME)); + } + } + + public static void setSubVelocityContext(VelocityContext context, GenTable genTable) + { + GenTable subTable = genTable.getSubTable(); + String subTableName = genTable.getSubTableName(); + String subTableFkName = genTable.getSubTableFkName(); + String subClassName = genTable.getSubTable().getClassName(); + String subTableFkClassName = StringUtils.convertToCamelCase(subTableFkName); + + context.put("subTable", subTable); + context.put("subTableName", subTableName); + context.put("subTableFkName", subTableFkName); + context.put("subTableFkClassName", subTableFkClassName); + context.put("subTableFkclassName", StringUtils.uncapitalize(subTableFkClassName)); + context.put("subClassName", subClassName); + context.put("subclassName", StringUtils.uncapitalize(subClassName)); + context.put("subImportList", getImportList(genTable.getSubTable())); + } + + /** + * 获取模板信息 + * + * @return 模板列表 + */ + public static List getTemplateList(String tplCategory) + { + List templates = new ArrayList(); + templates.add(VM_PATH+"/java/domain.java.vm"); + templates.add(VM_PATH+"/java/mapper.java.vm"); + templates.add(VM_PATH+"/java/service.java.vm"); + templates.add(VM_PATH+"/java/serviceImpl.java.vm"); + templates.add(VM_PATH+"/java/controller.java.vm"); + templates.add(VM_PATH+"/xml/mapper.xml.vm"); + templates.add(VM_PATH+"/sql/sql.vm"); + templates.add(VM_PATH+"/js/api.js.vm"); + if (GenConstants.TPL_CRUD.equals(tplCategory)) + { + templates.add(VM_PATH+"/vue/index.vue.vm"); + } + else if (GenConstants.TPL_TREE.equals(tplCategory)) + { + templates.add(VM_PATH+"/vue/index-tree.vue.vm"); + } + else if (GenConstants.TPL_SUB.equals(tplCategory)) + { + templates.add(VM_PATH+"/vue/index.vue.vm"); + templates.add(VM_PATH+"/java/sub-domain.java.vm"); + } + return templates; + } + + /** + * 获取文件名 + */ + public static String getFileName(String template, GenTable genTable) + { + // 文件名称 + String fileName = ""; + // 包路径 + String packageName = genTable.getPackageName(); + // 模块名 + String moduleName = genTable.getModuleName(); + // 大写类名 + String className = genTable.getClassName(); + // 业务名称 + String businessName = genTable.getBusinessName(); + + String javaPath = PROJECT_PATH + "/" + StringUtils.replace(packageName, ".", "/"); + String mybatisPath = MYBATIS_PATH + "/" + moduleName; + String vuePath = "vue"; + + if (template.contains("domain.java.vm")) + { + fileName = StringUtils.format("{}/domain/{}.java", javaPath, className); + } + if (template.contains("sub-domain.java.vm") && StringUtils.equals(GenConstants.TPL_SUB, genTable.getTplCategory())) + { + fileName = StringUtils.format("{}/domain/{}.java", javaPath, genTable.getSubTable().getClassName()); + } + else if (template.contains("mapper.java.vm")) + { + fileName = StringUtils.format("{}/mapper/{}Mapper.java", javaPath, className); + } + else if (template.contains("service.java.vm")) + { + fileName = StringUtils.format("{}/service/I{}Service.java", javaPath, className); + } + else if (template.contains("serviceImpl.java.vm")) + { + fileName = StringUtils.format("{}/service/impl/{}ServiceImpl.java", javaPath, className); + } + else if (template.contains("controller.java.vm")) + { + fileName = StringUtils.format("{}/controller/{}Controller.java", javaPath, className); + } + else if (template.contains("mapper.xml.vm")) + { + fileName = StringUtils.format("{}/{}Mapper.xml", mybatisPath, className); + } + else if (template.contains("sql.vm")) + { + fileName = businessName + "Menu.sql"; + } + else if (template.contains("api.js.vm")) + { + fileName = StringUtils.format("{}/api/{}/{}.js", vuePath, moduleName, businessName); + } + else if (template.contains("index.vue.vm")) + { + fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName); + } + else if (template.contains("index-tree.vue.vm")) + { + fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName); + } + return fileName; + } + + /** + * 获取包前缀 + * + * @param packageName 包名称 + * @return 包前缀名称 + */ + public static String getPackagePrefix(String packageName) + { + int lastIndex = packageName.lastIndexOf("."); + return StringUtils.substring(packageName, 0, lastIndex); + } + + /** + * 根据列类型获取导入包 + * + * @param genTable 业务表对象 + * @return 返回需要导入的包列表 + */ + public static HashSet getImportList(GenTable genTable) + { + List columns = genTable.getColumns(); + GenTable subGenTable = genTable.getSubTable(); + HashSet importList = new HashSet(); + if (StringUtils.isNotNull(subGenTable)) + { + importList.add("java.util.List"); + } + for (GenTableColumn column : columns) + { + if (!column.isSuperColumn() && GenConstants.TYPE_DATE.equals(column.getJavaType())) + { + importList.add("java.util.Date"); + importList.add("com.fasterxml.jackson.annotation.JsonFormat"); + } + else if (!column.isSuperColumn() && GenConstants.TYPE_BIGDECIMAL.equals(column.getJavaType())) + { + importList.add("java.math.BigDecimal"); + } + } + return importList; + } + + /** + * 根据列类型获取字典组 + * + * @param genTable 业务表对象 + * @return 返回字典组 + */ + public static String getDicts(GenTable genTable) + { + List columns = genTable.getColumns(); + Set dicts = new HashSet(); + addDicts(dicts, columns); + if (StringUtils.isNotNull(genTable.getSubTable())) + { + List subColumns = genTable.getSubTable().getColumns(); + addDicts(dicts, subColumns); + } + return StringUtils.join(dicts, ", "); + } + + /** + * 添加字典列表 + * + * @param dicts 字典列表 + * @param columns 列集合 + */ + public static void addDicts(Set dicts, List columns) + { + for (GenTableColumn column : columns) + { + if (!column.isSuperColumn() && StringUtils.isNotEmpty(column.getDictType()) && StringUtils.equalsAny( + column.getHtmlType(), + new String[] { GenConstants.HTML_SELECT, GenConstants.HTML_RADIO, GenConstants.HTML_CHECKBOX })) + { + dicts.add("'" + column.getDictType() + "'"); + } + } + } + + /** + * 获取权限前缀 + * + * @param moduleName 模块名称 + * @param businessName 业务名称 + * @return 返回权限前缀 + */ + public static String getPermissionPrefix(String moduleName, String businessName) + { + return StringUtils.format("{}:{}", moduleName, businessName); + } + + /** + * 获取上级菜单ID字段 + * + * @param paramsObj 生成其他选项 + * @return 上级菜单ID字段 + */ + public static String getParentMenuId(JSONObject paramsObj) + { + if (StringUtils.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.PARENT_MENU_ID) + && StringUtils.isNotEmpty(paramsObj.getString(GenConstants.PARENT_MENU_ID))) + { + return paramsObj.getString(GenConstants.PARENT_MENU_ID); + } + return DEFAULT_PARENT_MENU_ID; + } + + /** + * 获取树编码 + * + * @param paramsObj 生成其他选项 + * @return 树编码 + */ + public static String getTreecode(JSONObject paramsObj) + { + if (paramsObj.containsKey(GenConstants.TREE_CODE)) + { + return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_CODE)); + } + return StringUtils.EMPTY; + } + + /** + * 获取树父编码 + * + * @param paramsObj 生成其他选项 + * @return 树父编码 + */ + public static String getTreeParentCode(JSONObject paramsObj) + { + if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) + { + return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_PARENT_CODE)); + } + return StringUtils.EMPTY; + } + + /** + * 获取树名称 + * + * @param paramsObj 生成其他选项 + * @return 树名称 + */ + public static String getTreeName(JSONObject paramsObj) + { + if (paramsObj.containsKey(GenConstants.TREE_NAME)) + { + return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_NAME)); + } + return StringUtils.EMPTY; + } + + /** + * 获取需要在哪一列上面显示展开按钮 + * + * @param genTable 业务表对象 + * @return 展开按钮列序号 + */ + public static int getExpandColumn(GenTable genTable) + { + String options = genTable.getOptions(); + JSONObject paramsObj = JSON.parseObject(options); + String treeName = paramsObj.getString(GenConstants.TREE_NAME); + int num = 0; + for (GenTableColumn column : genTable.getColumns()) + { + if (column.isList()) + { + num++; + String columnName = column.getColumnName(); + if (columnName.equals(treeName)) + { + break; + } + } + } + return num; + } +} diff --git a/ruoyi-generator/src/main/resources/generator.yml b/ruoyi-generator/src/main/resources/generator.yml new file mode 100644 index 00000000..0bc255a7 --- /dev/null +++ b/ruoyi-generator/src/main/resources/generator.yml @@ -0,0 +1,10 @@ +# 代码生成 +gen: + # 作者 + author: chenqp + # 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool + packageName: com.ruoyi.system + # 自动去除表前缀,默认是false + autoRemovePre: false + # 表前缀(生成类名不会包含表前缀,多个用逗号分隔) + tablePrefix: sys_ \ No newline at end of file diff --git a/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml b/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml new file mode 100644 index 00000000..3c24013c --- /dev/null +++ b/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select column_id, table_id, column_name, column_comment, column_type, java_type, java_field, is_pk, is_increment, is_required, is_insert, is_edit, is_list, is_query, query_type, html_type, dict_type, sort, create_by, create_time, update_by, update_time from gen_table_column + + + + + + + + insert into gen_table_column ( + table_id, + column_name, + column_comment, + column_type, + java_type, + java_field, + is_pk, + is_increment, + is_required, + is_insert, + is_edit, + is_list, + is_query, + query_type, + html_type, + dict_type, + sort, + create_by, + create_time + )values( + #{tableId}, + #{columnName}, + #{columnComment}, + #{columnType}, + #{javaType}, + #{javaField}, + #{isPk}, + #{isIncrement}, + #{isRequired}, + #{isInsert}, + #{isEdit}, + #{isList}, + #{isQuery}, + #{queryType}, + #{htmlType}, + #{dictType}, + #{sort}, + #{createBy}, + sysdate() + ) + + + + update gen_table_column + + column_comment = #{columnComment}, + java_type = #{javaType}, + java_field = #{javaField}, + is_insert = #{isInsert}, + is_edit = #{isEdit}, + is_list = #{isList}, + is_query = #{isQuery}, + is_required = #{isRequired}, + query_type = #{queryType}, + html_type = #{htmlType}, + dict_type = #{dictType}, + sort = #{sort}, + update_by = #{updateBy}, + update_time = sysdate() + + where column_id = #{columnId} + + + + delete from gen_table_column where table_id in + + #{tableId} + + + + + delete from gen_table_column where column_id in + + #{item.columnId} + + + + diff --git a/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml b/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml new file mode 100644 index 00000000..b605e906 --- /dev/null +++ b/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml @@ -0,0 +1,202 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select table_id, table_name, table_comment, sub_table_name, sub_table_fk_name, class_name, tpl_category, package_name, module_name, business_name, function_name, function_author, gen_type, gen_path, options, create_by, create_time, update_by, update_time, remark from gen_table + + + + + + + + + + + + + + + + + + insert into gen_table ( + table_name, + table_comment, + class_name, + tpl_category, + package_name, + module_name, + business_name, + function_name, + function_author, + gen_type, + gen_path, + remark, + create_by, + create_time + )values( + #{tableName}, + #{tableComment}, + #{className}, + #{tplCategory}, + #{packageName}, + #{moduleName}, + #{businessName}, + #{functionName}, + #{functionAuthor}, + #{genType}, + #{genPath}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + update gen_table + + table_name = #{tableName}, + table_comment = #{tableComment}, + sub_table_name = #{subTableName}, + sub_table_fk_name = #{subTableFkName}, + class_name = #{className}, + function_author = #{functionAuthor}, + gen_type = #{genType}, + gen_path = #{genPath}, + tpl_category = #{tplCategory}, + package_name = #{packageName}, + module_name = #{moduleName}, + business_name = #{businessName}, + function_name = #{functionName}, + options = #{options}, + update_by = #{updateBy}, + remark = #{remark}, + update_time = sysdate() + + where table_id = #{tableId} + + + + delete from gen_table where table_id in + + #{tableId} + + + + \ No newline at end of file diff --git a/ruoyi-generator/src/main/resources/mybatisplusvm/java/controller.java.vm b/ruoyi-generator/src/main/resources/mybatisplusvm/java/controller.java.vm new file mode 100644 index 00000000..d2894379 --- /dev/null +++ b/ruoyi-generator/src/main/resources/mybatisplusvm/java/controller.java.vm @@ -0,0 +1,129 @@ +package ${packageName}.controller; + +import java.util.List; + +import io.swagger.annotations.*; +import com.baomidou.mybatisplus.core.metadata.IPage; + +import javax.servlet.http.HttpServletResponse; + +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.BusinessType; +import ${packageName}.domain.${ClassName}; +import ${packageName}.service.I${ClassName}Service; +import com.ruoyi.common.utils.poi.ExcelUtil; +#if($table.crud || $table.sub) +import com.ruoyi.common.core.page.TableDataInfo; +#elseif($table.tree) +#end + +/** + * ${functionName}Controller + * + * @author ${author} + * @date ${datetime} + */ +@RestController +@RequestMapping("/${moduleName}/${businessName}") +@Api(tags = {"【${functionName}】Controller"}) +public class ${ClassName}Controller extends BaseController { + @Autowired + private I${ClassName}Service ${className}Service; + +/** + * 分页查询${functionName}列表 + */ +@PreAuthorize("@ss.hasPermi('${permissionPrefix}:list')") +@GetMapping("/list") +@ApiOperation("分页查询${functionName}列表") + #if($table.crud || $table.sub) + public TableDataInfo list(${ClassName} ${className}) { + IPage<${ClassName}> list = ${className}Service.selectList(getPage(), ${className}); + return getDataTable(list); + } + #elseif($table.tree) + public AjaxResult list(${ClassName} ${className}) { + List<${ClassName}> list = ${className}Service.select${ClassName}List(${className}); + return AjaxResult.success(list); + } + #end + + /** + * 查询${functionName}列表 + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:list')") + @GetMapping("/list/all") + @ApiOperation("查询${functionName}列表") + public AjaxResult listAll(${ClassName} ${className}) { + List<${ClassName}> list = ${className}Service.selectListAll(${className}); + return success(list); + } + + /** + * 导出${functionName}列表 + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:export')") + @Log(title = "${functionName}", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @ApiOperation("导出${functionName}列表Excel") + public void export(HttpServletResponse response, ${ClassName} ${className}) { + List<${ClassName}> list = ${className}Service.selectListAll(${className}); + ExcelUtil<${ClassName}> util = new ExcelUtil<${ClassName}>(${ClassName}. class); + util.exportExcel(response, list, "${functionName}数据"); + } + + /** + * 获取${functionName}详细信息 + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:query')") + @GetMapping(value = "/{${pkColumn.javaField}}") + @ApiOperation("获取${functionName}详细信息") + public AjaxResult getInfo(@PathVariable("${pkColumn.javaField}") ${pkColumn.javaType} ${pkColumn.javaField}) { + return success(${className}Service.selectById(${pkColumn.javaField})); + } + + /** + * 新增${functionName} + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:add')") + @Log(title = "${functionName}", businessType = BusinessType.INSERT) + @PostMapping + @ApiOperation("新增${functionName}") + public AjaxResult add(@RequestBody ${ClassName} ${className}) { + return toAjax(${className}Service.insert(${className})); + } + + /** + * 修改${functionName} + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:edit')") + @Log(title = "${functionName}", businessType = BusinessType.UPDATE) + @PutMapping + @ApiOperation("修改${functionName}") + public AjaxResult edit(@RequestBody ${ClassName} ${className}) { + return toAjax(${className}Service.update(${className})); + } + + /** + * 删除${functionName} + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:remove')") + @Log(title = "${functionName}", businessType = BusinessType.DELETE) + @DeleteMapping("/{${pkColumn.javaField}s}") + @ApiOperation("删除${functionName}") + public AjaxResult remove(@PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s) { + return toAjax(${className}Service.deleteByIds(${pkColumn.javaField}s)); + } +} diff --git a/ruoyi-generator/src/main/resources/mybatisplusvm/java/domain.java.vm b/ruoyi-generator/src/main/resources/mybatisplusvm/java/domain.java.vm new file mode 100644 index 00000000..23249f21 --- /dev/null +++ b/ruoyi-generator/src/main/resources/mybatisplusvm/java/domain.java.vm @@ -0,0 +1,72 @@ +package ${packageName}.domain; + +#foreach ($import in $importList) +import ${import}; +#end +import com.ruoyi.common.annotation.Excel; +#if($table.crud || $table.sub) +import com.ruoyi.common.core.domain.BaseEntity; +#elseif($table.tree) +import com.ruoyi.common.core.domain.TreeEntity; +#end +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import com.baomidou.mybatisplus.annotation.TableName; +/** + * ${functionName}对象 ${tableName} + * + * @author ${author} + * @date ${datetime} + */ +#if($table.crud || $table.sub) + #set($Entity="BaseEntity") +#elseif($table.tree) + #set($Entity="TreeEntity") +#end + +@ApiModel(value = "${ClassName}", description = "${functionName}对象 ${tableName}") +@Data +@TableName(value = "${tableName}", autoResultMap = true) +public class ${ClassName} extends ${Entity} +{ + private static final long serialVersionUID=1L; + +#foreach ($column in $columns) +#if(!$table.isSuperColumn($column.javaField)) + /** $column.columnComment */ +#if($column.list) +#set($parentheseIndex=$column.columnComment.indexOf("(")) +#if($parentheseIndex != -1) +#set($comment=$column.columnComment.substring(0, $parentheseIndex)) +#else +#set($comment=$column.columnComment) +#end +#if($parentheseIndex != -1) + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") +#elseif($column.javaType == 'Date') + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd") +#else + @Excel(name = "${comment}") +#end +#end + @ApiModelProperty(value = "$column.columnComment") + private $column.javaType $column.javaField; + +#end +#end +#if($table.sub) +/** $table.subTable.functionName信息 */ + private List<${subClassName}> ${subclassName}List; + +#end +#foreach ($column in $columns) + #if(!$table.isSuperColumn($column.javaField)) + #if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) + #set($AttrName=$column.javaField) + #else + #set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) + #end + #end#end +} diff --git a/ruoyi-generator/src/main/resources/mybatisplusvm/java/mapper.java.vm b/ruoyi-generator/src/main/resources/mybatisplusvm/java/mapper.java.vm new file mode 100644 index 00000000..a46b08e2 --- /dev/null +++ b/ruoyi-generator/src/main/resources/mybatisplusvm/java/mapper.java.vm @@ -0,0 +1,36 @@ +package ${packageName}.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import java.util.List; +import org.apache.ibatis.annotations.Param; +import ${packageName}.domain.${ClassName}; +#if($table.sub) +import ${packageName}.domain.${subClassName}; +#end + +/** + * ${functionName}Mapper接口 + * + * @author ${author} + * @date ${datetime} + */ +public interface ${ClassName}Mapper extends BaseMapper<${ClassName}> +{ + /** + * 分页查询${functionName}列表 + * + * param page 分页信息 + * @param ${className} ${functionName}信息 + * @return ${functionName}集合 + */ + public IPage<${ClassName}> select${ClassName}Page(IPage<${ClassName}> page,@Param("entity") ${ClassName} ${className}); + + /** + * 查询所有${functionName}列表 + * + * @param ${className} ${functionName}信息 + * @return ${functionName}集合 + */ + public List<${ClassName}> select${ClassName}List(${ClassName} ${className}); +} diff --git a/ruoyi-generator/src/main/resources/mybatisplusvm/java/service.java.vm b/ruoyi-generator/src/main/resources/mybatisplusvm/java/service.java.vm new file mode 100644 index 00000000..5cda17db --- /dev/null +++ b/ruoyi-generator/src/main/resources/mybatisplusvm/java/service.java.vm @@ -0,0 +1,71 @@ +package ${packageName}.service; + +import java.util.List; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import ${packageName}.domain.${ClassName}; + +/** + * ${functionName}Service接口 + * + * @author ${author} + * @date ${datetime} + */ +public interface I${ClassName}Service extends IService<${ClassName}> +{ + /** + * 查询${functionName} + * + * @param ${pkColumn.javaField} ${functionName}ID + * @return ${functionName} + */ + public ${ClassName} selectById(${pkColumn.javaType} ${pkColumn.javaField}); + + /** + * 分页查询${functionName}列表 + * + * @param ${className} ${functionName} + * @return ${functionName}集合 + */ + public IPage<${ClassName}> selectList(IPage<${ClassName}> page, ${ClassName} ${className}); + + /** + * 查询所有${functionName}列表 + * + * @param ${className} ${functionName} + * @return ${functionName}集合 + */ + public List<${ClassName}> selectListAll(${ClassName} ${className}); + + /** + * 新增${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ + public int insert(${ClassName} ${className}); + + /** + * 修改${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ + public int update(${ClassName} ${className}); + + /** + * 批量删除${functionName} + * + * @param ${pkColumn.javaField}s 需要删除的${functionName}ID + * @return 结果 + */ + public int deleteByIds(${pkColumn.javaType}[] ${pkColumn.javaField}s); + + /** + * 删除${functionName}信息 + * + * @param ${pkColumn.javaField} ${functionName}ID + * @return 结果 + */ + public int deleteById(${pkColumn.javaType} ${pkColumn.javaField}); +} diff --git a/ruoyi-generator/src/main/resources/mybatisplusvm/java/serviceImpl.java.vm b/ruoyi-generator/src/main/resources/mybatisplusvm/java/serviceImpl.java.vm new file mode 100644 index 00000000..77411bd9 --- /dev/null +++ b/ruoyi-generator/src/main/resources/mybatisplusvm/java/serviceImpl.java.vm @@ -0,0 +1,174 @@ +package ${packageName}.service.impl; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import java.util.Arrays; +import java.util.List; +#foreach ($column in $columns) +#if($column.javaField == 'createTime' || $column.javaField == 'updateTime') +import com.ruoyi.common.utils.DateUtils; +#break +#end +#end +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +#if($table.sub) +import java.util.ArrayList; +import com.ruoyi.common.utils.StringUtils; +import org.springframework.transaction.annotation.Transactional; +import ${packageName}.domain.${subClassName}; +#end +import ${packageName}.mapper.${ClassName}Mapper; +import ${packageName}.domain.${ClassName}; +import ${packageName}.service.I${ClassName}Service; + +/** + * ${functionName}Service业务层处理 + * + * @author ${author} + * @date ${datetime} + */ +@Service +public class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}> implements I${ClassName}Service +{ + @Autowired + private ${ClassName}Mapper ${className}Mapper; + + /** + * 查询${functionName} + * + * @param ${pkColumn.javaField} ${functionName}ID + * @return ${functionName} + */ + @Override + public ${ClassName} selectById(${pkColumn.javaType} ${pkColumn.javaField}) { + return ${className}Mapper.selectById(${pkColumn.javaField}); + } + + /** + * 分页查询${functionName}列表 + * + * @param ${className} ${functionName} + * @return ${functionName} + */ + @Override + public IPage<${ClassName}> selectList(IPage<${ClassName}> page, ${ClassName} ${className}) { + return ${className}Mapper.select${ClassName}Page(page, ${className}); + } + + /** + * 查询所有${functionName}列表 + * + * @param ${className} ${functionName} + * @return ${functionName} + */ + @Override + public List<${ClassName}> selectListAll(${ClassName} ${className}) { + return ${className}Mapper.select${ClassName}List(${className}); + } + + /** + * 新增${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int insert(${ClassName} ${className}) { +#foreach ($column in $columns) + #if($column.javaField == 'createTime') + ${className}.setCreateTime(DateUtils.getNowDate()); + #end +#end +#if($table.sub) + int rows = ${className}Mapper.insert(${className}); + insert${subClassName}(${className}); + return rows; +#else + return ${className}Mapper.insert(${className}); +#end + } + + /** + * 修改${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int update(${ClassName} ${className}) { +#foreach ($column in $columns) + #if($column.javaField == 'updateTime') + ${className}.setUpdateTime(DateUtils.getNowDate()); + #end +#end +#if($table.sub) + ${className}Mapper.delete${subClassName}By${subTableFkClassName}(${className}.get${pkColumn.capJavaField}()); + insert${subClassName}(${className}); +#end + return ${className}Mapper.updateById(${className}); + } + + /** + * 批量删除${functionName} + * + * @param ${pkColumn.javaField}s 需要删除的${functionName}ID + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int deleteByIds(${pkColumn.javaType}[] ${pkColumn.javaField}s) { +#if($table.sub) + ${className}Mapper.delete${subClassName}By${subTableFkClassName}s(${pkColumn.javaField}s); +#end + return ${className}Mapper.deleteBatchIds(Arrays.asList(${pkColumn.javaField}s)); + } + + /** + * 删除${functionName}信息 + * + * @param ${pkColumn.javaField} ${functionName}ID + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int deleteById(${pkColumn.javaType} ${pkColumn.javaField}) { +#if($table.sub) + ${className}Mapper.delete${subClassName}By${subTableFkClassName}(${pkColumn.javaField}); +#end + return ${className}Mapper.deleteById(${pkColumn.javaField}); + } +#if($table.sub) + + /** + * 新增${subTable.functionName}信息 + * + * @param ${className} ${functionName}对象 + */ + public void insert${subClassName}(${ClassName} ${className}) { + List<${subClassName}> ${subclassName}List = ${className}.get${subClassName}List(); + Long ${pkColumn.javaField} = ${className}.get${pkColumn.capJavaField}(); + if (StringUtils.isNotNull(${subclassName}List)) { + List<${subClassName}> list = new ArrayList<${subClassName}>(); + for (${subClassName} ${subclassName} :${subclassName}List) + { + ${subclassName}.set${subTableFkClassName}(${pkColumn.javaField}); + list.add(${subclassName}); + } + if (list.size() > 0) { + ${className}Mapper.batch${subClassName}(list); + } + } + } +#end +} diff --git a/ruoyi-generator/src/main/resources/mybatisplusvm/java/sub-domain.java.vm b/ruoyi-generator/src/main/resources/mybatisplusvm/java/sub-domain.java.vm new file mode 100644 index 00000000..ad3890a0 --- /dev/null +++ b/ruoyi-generator/src/main/resources/mybatisplusvm/java/sub-domain.java.vm @@ -0,0 +1,76 @@ +package ${packageName}.domain; + + #foreach ($import in $subImportList) + import ${import}; + #end +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * ${subTable.functionName}对象 ${subTableName} + * + * @author ${author} + * @date ${datetime} + */ +public class ${subClassName} extends BaseEntity + { +private static final long serialVersionUID=1L; + +#foreach ($column in $subTable.columns) + #if(!$table.isSuperColumn($column.javaField)) + /** $column.columnComment */ + #if($column.list) + #set($parentheseIndex=$column.columnComment.indexOf("(")) + #if($parentheseIndex != -1) + #set($comment=$column.columnComment.substring(0, $parentheseIndex)) + #else + #set($comment=$column.columnComment) + #end + #if($parentheseIndex != -1) + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") + #elseif($column.javaType == 'Date') + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd") + #else + @Excel(name = "${comment}") + #end + #end + private $column.javaType $column.javaField; + + #end +#end +#foreach ($column in $subTable.columns) + #if(!$table.isSuperColumn($column.javaField)) + #if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) + #set($AttrName=$column.javaField) + #else + #set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) + #end + public void set${AttrName}($column.javaType $column.javaField) + { + this.$column.javaField = $column.javaField; + } + + public $column.javaType get${AttrName}() + { + return $column.javaField; + } + #end +#end + +@Override +public String toString(){ + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + #foreach ($column in $subTable.columns) + #if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) + #set($AttrName=$column.javaField) + #else + #set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) + #end + .append("${column.javaField}",get${AttrName}()) + #end + .toString(); + } + } diff --git a/ruoyi-generator/src/main/resources/mybatisplusvm/js/api.js.vm b/ruoyi-generator/src/main/resources/mybatisplusvm/js/api.js.vm new file mode 100644 index 00000000..9295524a --- /dev/null +++ b/ruoyi-generator/src/main/resources/mybatisplusvm/js/api.js.vm @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询${functionName}列表 +export function list${BusinessName}(query) { + return request({ + url: '/${moduleName}/${businessName}/list', + method: 'get', + params: query + }) +} + +// 查询${functionName}详细 +export function get${BusinessName}(${pkColumn.javaField}) { + return request({ + url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField}, + method: 'get' + }) +} + +// 新增${functionName} +export function add${BusinessName}(data) { + return request({ + url: '/${moduleName}/${businessName}', + method: 'post', + data: data + }) +} + +// 修改${functionName} +export function update${BusinessName}(data) { + return request({ + url: '/${moduleName}/${businessName}', + method: 'put', + data: data + }) +} + +// 删除${functionName} +export function del${BusinessName}(${pkColumn.javaField}) { + return request({ + url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField}, + method: 'delete' + }) +} diff --git a/ruoyi-generator/src/main/resources/mybatisplusvm/sql/sql.vm b/ruoyi-generator/src/main/resources/mybatisplusvm/sql/sql.vm new file mode 100644 index 00000000..05755835 --- /dev/null +++ b/ruoyi-generator/src/main/resources/mybatisplusvm/sql/sql.vm @@ -0,0 +1,22 @@ +-- 菜单 SQL +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 'admin', sysdate(), '', null, '${functionName}菜单'); + +-- 按钮父菜单ID +SELECT @parentId := LAST_INSERT_ID(); + +-- 按钮 SQL +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}查询', @parentId, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}新增', @parentId, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}修改', @parentId, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}删除', @parentId, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}导出', @parentId, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 'admin', sysdate(), '', null, ''); \ No newline at end of file diff --git a/ruoyi-generator/src/main/resources/mybatisplusvm/vue/index-tree.vue.vm b/ruoyi-generator/src/main/resources/mybatisplusvm/vue/index-tree.vue.vm new file mode 100644 index 00000000..3def7045 --- /dev/null +++ b/ruoyi-generator/src/main/resources/mybatisplusvm/vue/index-tree.vue.vm @@ -0,0 +1,502 @@ + + + diff --git a/ruoyi-generator/src/main/resources/mybatisplusvm/vue/index.vue.vm b/ruoyi-generator/src/main/resources/mybatisplusvm/vue/index.vue.vm new file mode 100644 index 00000000..e9a1fae1 --- /dev/null +++ b/ruoyi-generator/src/main/resources/mybatisplusvm/vue/index.vue.vm @@ -0,0 +1,598 @@ + + + diff --git a/ruoyi-generator/src/main/resources/mybatisplusvm/vue/v3/index-tree.vue.vm b/ruoyi-generator/src/main/resources/mybatisplusvm/vue/v3/index-tree.vue.vm new file mode 100644 index 00000000..862297c7 --- /dev/null +++ b/ruoyi-generator/src/main/resources/mybatisplusvm/vue/v3/index-tree.vue.vm @@ -0,0 +1,486 @@ + + + diff --git a/ruoyi-generator/src/main/resources/mybatisplusvm/vue/v3/index.vue.vm b/ruoyi-generator/src/main/resources/mybatisplusvm/vue/v3/index.vue.vm new file mode 100644 index 00000000..f66cc3b8 --- /dev/null +++ b/ruoyi-generator/src/main/resources/mybatisplusvm/vue/v3/index.vue.vm @@ -0,0 +1,596 @@ + + + diff --git a/ruoyi-generator/src/main/resources/mybatisplusvm/vue/v3/readme.txt b/ruoyi-generator/src/main/resources/mybatisplusvm/vue/v3/readme.txt new file mode 100644 index 00000000..99239bb5 --- /dev/null +++ b/ruoyi-generator/src/main/resources/mybatisplusvm/vue/v3/readme.txt @@ -0,0 +1 @@ +ʹõRuoYi-Vue3ǰˣôҪһ´Ŀ¼ģindex.vue.vmindex-tree.vue.vmļϼvueĿ¼ \ No newline at end of file diff --git a/ruoyi-generator/src/main/resources/mybatisplusvm/xml/mapper.xml.vm b/ruoyi-generator/src/main/resources/mybatisplusvm/xml/mapper.xml.vm new file mode 100644 index 00000000..4fc7d573 --- /dev/null +++ b/ruoyi-generator/src/main/resources/mybatisplusvm/xml/mapper.xml.vm @@ -0,0 +1,109 @@ + + + + + +#foreach ($column in $columns) + +#end + +#if($table.sub) + + + + + + +#foreach ($column in $subTable.columns) + +#end + +#end + + + select#foreach($column in $columns) $column.columnName#if($foreach.count != $columns.size()),#end#end from ${tableName} + + + + + + diff --git a/ruoyi-generator/src/main/resources/vm/java/controller.java.vm b/ruoyi-generator/src/main/resources/vm/java/controller.java.vm new file mode 100644 index 00000000..5955c6f6 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/java/controller.java.vm @@ -0,0 +1,108 @@ +package ${packageName}.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.BusinessType; +import ${packageName}.domain.${ClassName}; +import ${packageName}.service.I${ClassName}Service; +import com.ruoyi.common.utils.poi.ExcelUtil; +#if($table.crud || $table.sub) +import com.ruoyi.common.core.page.TableDataInfo; +#elseif($table.tree) +#end + +/** + * ${functionName}Controller + * + * @author ${author} + * @date ${datetime} + */ +@RestController +@RequestMapping("/${moduleName}/${businessName}") +public class ${ClassName}Controller extends BaseController { + @Autowired + private I${ClassName}Service ${className}Service; + +/** + * 查询${functionName}列表 + */ +@PreAuthorize("@ss.hasPermi('${permissionPrefix}:list')") +@GetMapping("/list") + #if($table.crud || $table.sub) + public TableDataInfo list(${ClassName} ${className}) { + startPage(); + List<${ClassName}> list = ${className}Service.select${ClassName}List(${className}); + return getDataTable(list); + } + #elseif($table.tree) + public AjaxResult list(${ClassName} ${className}) { + List<${ClassName}> list = ${className}Service.select${ClassName}List(${className}); + return success(list); + } + #end + + /** + * 导出${functionName}列表 + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:export')") + @Log(title = "${functionName}", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, ${ClassName} ${className}) { + List<${ClassName}> list = ${className}Service.select${ClassName}List(${className}); + ExcelUtil<${ClassName}> util = new ExcelUtil<${ClassName}>(${ClassName}. class); + util.exportExcel(response, list, "${functionName}数据"); + } + + /** + * 获取${functionName}详细信息 + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:query')") + @GetMapping(value = "/{${pkColumn.javaField}}") + public AjaxResult getInfo(@PathVariable("${pkColumn.javaField}") ${pkColumn.javaType} ${pkColumn.javaField}) { + return success(${className}Service.select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField})); + } + + /** + * 新增${functionName} + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:add')") + @Log(title = "${functionName}", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody ${ClassName} ${className}) { + return toAjax(${className}Service.insert${ClassName}(${className})); + } + + /** + * 修改${functionName} + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:edit')") + @Log(title = "${functionName}", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody ${ClassName} ${className}) { + return toAjax(${className}Service.update${ClassName}(${className})); + } + + /** + * 删除${functionName} + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:remove')") + @Log(title = "${functionName}", businessType = BusinessType.DELETE) + @DeleteMapping("/{${pkColumn.javaField}s}") + public AjaxResult remove(@PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s) { + return toAjax(${className}Service.delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaField}s)); + } +} diff --git a/ruoyi-generator/src/main/resources/vm/java/domain.java.vm b/ruoyi-generator/src/main/resources/vm/java/domain.java.vm new file mode 100644 index 00000000..bd51c177 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/java/domain.java.vm @@ -0,0 +1,105 @@ +package ${packageName}.domain; + +#foreach ($import in $importList) +import ${import}; +#end +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +#if($table.crud || $table.sub) +import com.ruoyi.common.core.domain.BaseEntity; +#elseif($table.tree) +import com.ruoyi.common.core.domain.TreeEntity; +#end + +/** + * ${functionName}对象 ${tableName} + * + * @author ${author} + * @date ${datetime} + */ +#if($table.crud || $table.sub) +#set($Entity="BaseEntity") +#elseif($table.tree) +#set($Entity="TreeEntity") +#end +public class ${ClassName} extends ${Entity} +{ + private static final long serialVersionUID = 1L; + +#foreach ($column in $columns) +#if(!$table.isSuperColumn($column.javaField)) + /** $column.columnComment */ +#if($column.list) +#set($parentheseIndex=$column.columnComment.indexOf("(")) +#if($parentheseIndex != -1) +#set($comment=$column.columnComment.substring(0, $parentheseIndex)) +#else +#set($comment=$column.columnComment) +#end +#if($parentheseIndex != -1) + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") +#elseif($column.javaType == 'Date') + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd") +#else + @Excel(name = "${comment}") +#end +#end + private $column.javaType $column.javaField; + +#end +#end +#if($table.sub) + /** $table.subTable.functionName信息 */ + private List<${subClassName}> ${subclassName}List; + +#end +#foreach ($column in $columns) +#if(!$table.isSuperColumn($column.javaField)) +#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) +#set($AttrName=$column.javaField) +#else +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#end + public void set${AttrName}($column.javaType $column.javaField) + { + this.$column.javaField = $column.javaField; + } + + public $column.javaType get${AttrName}() + { + return $column.javaField; + } +#end +#end + +#if($table.sub) + public List<${subClassName}> get${subClassName}List() + { + return ${subclassName}List; + } + + public void set${subClassName}List(List<${subClassName}> ${subclassName}List) + { + this.${subclassName}List = ${subclassName}List; + } + +#end + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) +#foreach ($column in $columns) +#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) +#set($AttrName=$column.javaField) +#else +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#end + .append("${column.javaField}", get${AttrName}()) +#end +#if($table.sub) + .append("${subclassName}List", get${subClassName}List()) +#end + .toString(); + } +} diff --git a/ruoyi-generator/src/main/resources/vm/java/mapper.java.vm b/ruoyi-generator/src/main/resources/vm/java/mapper.java.vm new file mode 100644 index 00000000..7e7d7c26 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/java/mapper.java.vm @@ -0,0 +1,91 @@ +package ${packageName}.mapper; + +import java.util.List; +import ${packageName}.domain.${ClassName}; +#if($table.sub) +import ${packageName}.domain.${subClassName}; +#end + +/** + * ${functionName}Mapper接口 + * + * @author ${author} + * @date ${datetime} + */ +public interface ${ClassName}Mapper +{ + /** + * 查询${functionName} + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return ${functionName} + */ + public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); + + /** + * 查询${functionName}列表 + * + * @param ${className} ${functionName} + * @return ${functionName}集合 + */ + public List<${ClassName}> select${ClassName}List(${ClassName} ${className}); + + /** + * 新增${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ + public int insert${ClassName}(${ClassName} ${className}); + + /** + * 修改${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ + public int update${ClassName}(${ClassName} ${className}); + + /** + * 删除${functionName} + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return 结果 + */ + public int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); + + /** + * 批量删除${functionName} + * + * @param ${pkColumn.javaField}s 需要删除的数据主键集合 + * @return 结果 + */ + public int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s); +#if($table.sub) + + /** + * 批量删除${subTable.functionName} + * + * @param ${pkColumn.javaField}s 需要删除的数据主键集合 + * @return 结果 + */ + public int delete${subClassName}By${subTableFkClassName}s(${pkColumn.javaType}[] ${pkColumn.javaField}s); + + /** + * 批量新增${subTable.functionName} + * + * @param ${subclassName}List ${subTable.functionName}列表 + * @return 结果 + */ + public int batch${subClassName}(List<${subClassName}> ${subclassName}List); + + + /** + * 通过${functionName}主键删除${subTable.functionName}信息 + * + * @param ${pkColumn.javaField} ${functionName}ID + * @return 结果 + */ + public int delete${subClassName}By${subTableFkClassName}(${pkColumn.javaType} ${pkColumn.javaField}); +#end +} diff --git a/ruoyi-generator/src/main/resources/vm/java/service.java.vm b/ruoyi-generator/src/main/resources/vm/java/service.java.vm new file mode 100644 index 00000000..264882b2 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/java/service.java.vm @@ -0,0 +1,61 @@ +package ${packageName}.service; + +import java.util.List; +import ${packageName}.domain.${ClassName}; + +/** + * ${functionName}Service接口 + * + * @author ${author} + * @date ${datetime} + */ +public interface I${ClassName}Service +{ + /** + * 查询${functionName} + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return ${functionName} + */ + public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); + + /** + * 查询${functionName}列表 + * + * @param ${className} ${functionName} + * @return ${functionName}集合 + */ + public List<${ClassName}> select${ClassName}List(${ClassName} ${className}); + + /** + * 新增${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ + public int insert${ClassName}(${ClassName} ${className}); + + /** + * 修改${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ + public int update${ClassName}(${ClassName} ${className}); + + /** + * 批量删除${functionName} + * + * @param ${pkColumn.javaField}s 需要删除的${functionName}主键集合 + * @return 结果 + */ + public int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s); + + /** + * 删除${functionName}信息 + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return 结果 + */ + public int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); +} diff --git a/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm b/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm new file mode 100644 index 00000000..14746e1a --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm @@ -0,0 +1,169 @@ +package ${packageName}.service.impl; + +import java.util.List; +#foreach ($column in $columns) +#if($column.javaField == 'createTime' || $column.javaField == 'updateTime') +import com.ruoyi.common.utils.DateUtils; +#break +#end +#end +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +#if($table.sub) +import java.util.ArrayList; +import com.ruoyi.common.utils.StringUtils; +import org.springframework.transaction.annotation.Transactional; +import ${packageName}.domain.${subClassName}; +#end +import ${packageName}.mapper.${ClassName}Mapper; +import ${packageName}.domain.${ClassName}; +import ${packageName}.service.I${ClassName}Service; + +/** + * ${functionName}Service业务层处理 + * + * @author ${author} + * @date ${datetime} + */ +@Service +public class ${ClassName}ServiceImpl implements I${ClassName}Service +{ + @Autowired + private ${ClassName}Mapper ${className}Mapper; + + /** + * 查询${functionName} + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return ${functionName} + */ + @Override + public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}) + { + return ${className}Mapper.select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField}); + } + + /** + * 查询${functionName}列表 + * + * @param ${className} ${functionName} + * @return ${functionName} + */ + @Override + public List<${ClassName}> select${ClassName}List(${ClassName} ${className}) + { + return ${className}Mapper.select${ClassName}List(${className}); + } + + /** + * 新增${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int insert${ClassName}(${ClassName} ${className}) + { +#foreach ($column in $columns) +#if($column.javaField == 'createTime') + ${className}.setCreateTime(DateUtils.getNowDate()); +#end +#end +#if($table.sub) + int rows = ${className}Mapper.insert${ClassName}(${className}); + insert${subClassName}(${className}); + return rows; +#else + return ${className}Mapper.insert${ClassName}(${className}); +#end + } + + /** + * 修改${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int update${ClassName}(${ClassName} ${className}) + { +#foreach ($column in $columns) +#if($column.javaField == 'updateTime') + ${className}.setUpdateTime(DateUtils.getNowDate()); +#end +#end +#if($table.sub) + ${className}Mapper.delete${subClassName}By${subTableFkClassName}(${className}.get${pkColumn.capJavaField}()); + insert${subClassName}(${className}); +#end + return ${className}Mapper.update${ClassName}(${className}); + } + + /** + * 批量删除${functionName} + * + * @param ${pkColumn.javaField}s 需要删除的${functionName}主键 + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s) + { +#if($table.sub) + ${className}Mapper.delete${subClassName}By${subTableFkClassName}s(${pkColumn.javaField}s); +#end + return ${className}Mapper.delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaField}s); + } + + /** + * 删除${functionName}信息 + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}) + { +#if($table.sub) + ${className}Mapper.delete${subClassName}By${subTableFkClassName}(${pkColumn.javaField}); +#end + return ${className}Mapper.delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField}); + } +#if($table.sub) + + /** + * 新增${subTable.functionName}信息 + * + * @param ${className} ${functionName}对象 + */ + public void insert${subClassName}(${ClassName} ${className}) + { + List<${subClassName}> ${subclassName}List = ${className}.get${subClassName}List(); + ${pkColumn.javaType} ${pkColumn.javaField} = ${className}.get${pkColumn.capJavaField}(); + if (StringUtils.isNotNull(${subclassName}List)) + { + List<${subClassName}> list = new ArrayList<${subClassName}>(); + for (${subClassName} ${subclassName} : ${subclassName}List) + { + ${subclassName}.set${subTableFkClassName}(${pkColumn.javaField}); + list.add(${subclassName}); + } + if (list.size() > 0) + { + ${className}Mapper.batch${subClassName}(list); + } + } + } +#end +} diff --git a/ruoyi-generator/src/main/resources/vm/java/sub-domain.java.vm b/ruoyi-generator/src/main/resources/vm/java/sub-domain.java.vm new file mode 100644 index 00000000..a3f53eba --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/java/sub-domain.java.vm @@ -0,0 +1,76 @@ +package ${packageName}.domain; + +#foreach ($import in $subImportList) +import ${import}; +#end +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * ${subTable.functionName}对象 ${subTableName} + * + * @author ${author} + * @date ${datetime} + */ +public class ${subClassName} extends BaseEntity +{ + private static final long serialVersionUID = 1L; + +#foreach ($column in $subTable.columns) +#if(!$table.isSuperColumn($column.javaField)) + /** $column.columnComment */ +#if($column.list) +#set($parentheseIndex=$column.columnComment.indexOf("(")) +#if($parentheseIndex != -1) +#set($comment=$column.columnComment.substring(0, $parentheseIndex)) +#else +#set($comment=$column.columnComment) +#end +#if($parentheseIndex != -1) + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") +#elseif($column.javaType == 'Date') + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd") +#else + @Excel(name = "${comment}") +#end +#end + private $column.javaType $column.javaField; + +#end +#end +#foreach ($column in $subTable.columns) +#if(!$table.isSuperColumn($column.javaField)) +#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) +#set($AttrName=$column.javaField) +#else +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#end + public void set${AttrName}($column.javaType $column.javaField) + { + this.$column.javaField = $column.javaField; + } + + public $column.javaType get${AttrName}() + { + return $column.javaField; + } +#end +#end + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) +#foreach ($column in $subTable.columns) +#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) +#set($AttrName=$column.javaField) +#else +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#end + .append("${column.javaField}", get${AttrName}()) +#end + .toString(); + } +} diff --git a/ruoyi-generator/src/main/resources/vm/js/api.js.vm b/ruoyi-generator/src/main/resources/vm/js/api.js.vm new file mode 100644 index 00000000..9295524a --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/js/api.js.vm @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询${functionName}列表 +export function list${BusinessName}(query) { + return request({ + url: '/${moduleName}/${businessName}/list', + method: 'get', + params: query + }) +} + +// 查询${functionName}详细 +export function get${BusinessName}(${pkColumn.javaField}) { + return request({ + url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField}, + method: 'get' + }) +} + +// 新增${functionName} +export function add${BusinessName}(data) { + return request({ + url: '/${moduleName}/${businessName}', + method: 'post', + data: data + }) +} + +// 修改${functionName} +export function update${BusinessName}(data) { + return request({ + url: '/${moduleName}/${businessName}', + method: 'put', + data: data + }) +} + +// 删除${functionName} +export function del${BusinessName}(${pkColumn.javaField}) { + return request({ + url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField}, + method: 'delete' + }) +} diff --git a/ruoyi-generator/src/main/resources/vm/sql/sql.vm b/ruoyi-generator/src/main/resources/vm/sql/sql.vm new file mode 100644 index 00000000..05755835 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/sql/sql.vm @@ -0,0 +1,22 @@ +-- 菜单 SQL +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 'admin', sysdate(), '', null, '${functionName}菜单'); + +-- 按钮父菜单ID +SELECT @parentId := LAST_INSERT_ID(); + +-- 按钮 SQL +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}查询', @parentId, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}新增', @parentId, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}修改', @parentId, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}删除', @parentId, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}导出', @parentId, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 'admin', sysdate(), '', null, ''); \ No newline at end of file diff --git a/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm b/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm new file mode 100644 index 00000000..3def7045 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm @@ -0,0 +1,502 @@ + + + diff --git a/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm b/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm new file mode 100644 index 00000000..3f9a4b4a --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm @@ -0,0 +1,609 @@ + + + diff --git a/ruoyi-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm b/ruoyi-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm new file mode 100644 index 00000000..862297c7 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm @@ -0,0 +1,486 @@ + + + diff --git a/ruoyi-generator/src/main/resources/vm/vue/v3/index.vue.vm b/ruoyi-generator/src/main/resources/vm/vue/v3/index.vue.vm new file mode 100644 index 00000000..f66cc3b8 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/vue/v3/index.vue.vm @@ -0,0 +1,596 @@ + + + diff --git a/ruoyi-generator/src/main/resources/vm/vue/v3/readme.txt b/ruoyi-generator/src/main/resources/vm/vue/v3/readme.txt new file mode 100644 index 00000000..99239bb5 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/vue/v3/readme.txt @@ -0,0 +1 @@ +ʹõRuoYi-Vue3ǰˣôҪһ´Ŀ¼ģindex.vue.vmindex-tree.vue.vmļϼvueĿ¼ \ No newline at end of file diff --git a/ruoyi-generator/src/main/resources/vm/xml/mapper.xml.vm b/ruoyi-generator/src/main/resources/vm/xml/mapper.xml.vm new file mode 100644 index 00000000..0ceb3d85 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/xml/mapper.xml.vm @@ -0,0 +1,135 @@ + + + + + +#foreach ($column in $columns) + +#end + +#if($table.sub) + + + + + + +#foreach ($column in $subTable.columns) + +#end + +#end + + + select#foreach($column in $columns) $column.columnName#if($foreach.count != $columns.size()),#end#end from ${tableName} + + + + + + + + insert into ${tableName} + +#foreach($column in $columns) +#if($column.columnName != $pkColumn.columnName || !$pkColumn.increment) + $column.columnName, +#end +#end + + +#foreach($column in $columns) +#if($column.columnName != $pkColumn.columnName || !$pkColumn.increment) + #{$column.javaField}, +#end +#end + + + + + update ${tableName} + +#foreach($column in $columns) +#if($column.columnName != $pkColumn.columnName) + $column.columnName = #{$column.javaField}, +#end +#end + + where ${pkColumn.columnName} = #{${pkColumn.javaField}} + + + + delete from ${tableName} where ${pkColumn.columnName} = #{${pkColumn.javaField}} + + + + delete from ${tableName} where ${pkColumn.columnName} in + + #{${pkColumn.javaField}} + + +#if($table.sub) + + + delete from ${subTableName} where ${subTableFkName} in + + #{${subTableFkclassName}} + + + + + delete from ${subTableName} where ${subTableFkName} = #{${subTableFkclassName}} + + + + insert into ${subTableName}(#foreach($column in $subTable.columns) $column.columnName#if($foreach.count != $subTable.columns.size()),#end#end) values + + (#foreach($column in $subTable.columns) #{item.$column.javaField}#if($foreach.count != $subTable.columns.size()),#end#end) + + +#end + \ No newline at end of file diff --git a/ruoyi-generator/target/classes/com/ruoyi/generator/config/GenConfig.class b/ruoyi-generator/target/classes/com/ruoyi/generator/config/GenConfig.class new file mode 100644 index 00000000..90bf2a62 Binary files /dev/null and b/ruoyi-generator/target/classes/com/ruoyi/generator/config/GenConfig.class differ diff --git a/ruoyi-generator/target/classes/com/ruoyi/generator/controller/GenController.class b/ruoyi-generator/target/classes/com/ruoyi/generator/controller/GenController.class new file mode 100644 index 00000000..67a3a560 Binary files /dev/null and b/ruoyi-generator/target/classes/com/ruoyi/generator/controller/GenController.class differ diff --git a/ruoyi-generator/target/classes/com/ruoyi/generator/domain/GenTable.class b/ruoyi-generator/target/classes/com/ruoyi/generator/domain/GenTable.class new file mode 100644 index 00000000..54d637db Binary files /dev/null and b/ruoyi-generator/target/classes/com/ruoyi/generator/domain/GenTable.class differ diff --git a/ruoyi-generator/target/classes/com/ruoyi/generator/domain/GenTableColumn.class b/ruoyi-generator/target/classes/com/ruoyi/generator/domain/GenTableColumn.class new file mode 100644 index 00000000..790092df Binary files /dev/null and b/ruoyi-generator/target/classes/com/ruoyi/generator/domain/GenTableColumn.class differ diff --git a/ruoyi-generator/target/classes/com/ruoyi/generator/mapper/GenTableColumnMapper.class b/ruoyi-generator/target/classes/com/ruoyi/generator/mapper/GenTableColumnMapper.class new file mode 100644 index 00000000..1ac6c995 Binary files /dev/null and b/ruoyi-generator/target/classes/com/ruoyi/generator/mapper/GenTableColumnMapper.class differ diff --git a/ruoyi-generator/target/classes/com/ruoyi/generator/mapper/GenTableMapper.class b/ruoyi-generator/target/classes/com/ruoyi/generator/mapper/GenTableMapper.class new file mode 100644 index 00000000..faa99bc1 Binary files /dev/null and b/ruoyi-generator/target/classes/com/ruoyi/generator/mapper/GenTableMapper.class differ diff --git a/ruoyi-generator/target/classes/com/ruoyi/generator/service/GenTableColumnServiceImpl.class b/ruoyi-generator/target/classes/com/ruoyi/generator/service/GenTableColumnServiceImpl.class new file mode 100644 index 00000000..81d4a4e8 Binary files /dev/null and b/ruoyi-generator/target/classes/com/ruoyi/generator/service/GenTableColumnServiceImpl.class differ diff --git a/ruoyi-generator/target/classes/com/ruoyi/generator/service/GenTableServiceImpl.class b/ruoyi-generator/target/classes/com/ruoyi/generator/service/GenTableServiceImpl.class new file mode 100644 index 00000000..c713e687 Binary files /dev/null and b/ruoyi-generator/target/classes/com/ruoyi/generator/service/GenTableServiceImpl.class differ diff --git a/ruoyi-generator/target/classes/com/ruoyi/generator/service/IGenTableColumnService.class b/ruoyi-generator/target/classes/com/ruoyi/generator/service/IGenTableColumnService.class new file mode 100644 index 00000000..b5597ff7 Binary files /dev/null and b/ruoyi-generator/target/classes/com/ruoyi/generator/service/IGenTableColumnService.class differ diff --git a/ruoyi-generator/target/classes/com/ruoyi/generator/service/IGenTableService.class b/ruoyi-generator/target/classes/com/ruoyi/generator/service/IGenTableService.class new file mode 100644 index 00000000..24acd779 Binary files /dev/null and b/ruoyi-generator/target/classes/com/ruoyi/generator/service/IGenTableService.class differ diff --git a/ruoyi-generator/target/classes/com/ruoyi/generator/util/GenTableUtils.class b/ruoyi-generator/target/classes/com/ruoyi/generator/util/GenTableUtils.class new file mode 100644 index 00000000..6932a1bc Binary files /dev/null and b/ruoyi-generator/target/classes/com/ruoyi/generator/util/GenTableUtils.class differ diff --git a/ruoyi-generator/target/classes/com/ruoyi/generator/util/GenUtils.class b/ruoyi-generator/target/classes/com/ruoyi/generator/util/GenUtils.class new file mode 100644 index 00000000..058580d0 Binary files /dev/null and b/ruoyi-generator/target/classes/com/ruoyi/generator/util/GenUtils.class differ diff --git a/ruoyi-generator/target/classes/com/ruoyi/generator/util/VelocityInitializer.class b/ruoyi-generator/target/classes/com/ruoyi/generator/util/VelocityInitializer.class new file mode 100644 index 00000000..4caf94ef Binary files /dev/null and b/ruoyi-generator/target/classes/com/ruoyi/generator/util/VelocityInitializer.class differ diff --git a/ruoyi-generator/target/classes/com/ruoyi/generator/util/VelocityUtils.class b/ruoyi-generator/target/classes/com/ruoyi/generator/util/VelocityUtils.class new file mode 100644 index 00000000..7e2677a7 Binary files /dev/null and b/ruoyi-generator/target/classes/com/ruoyi/generator/util/VelocityUtils.class differ diff --git a/ruoyi-generator/target/classes/generator.yml b/ruoyi-generator/target/classes/generator.yml new file mode 100644 index 00000000..0bc255a7 --- /dev/null +++ b/ruoyi-generator/target/classes/generator.yml @@ -0,0 +1,10 @@ +# 代码生成 +gen: + # 作者 + author: chenqp + # 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool + packageName: com.ruoyi.system + # 自动去除表前缀,默认是false + autoRemovePre: false + # 表前缀(生成类名不会包含表前缀,多个用逗号分隔) + tablePrefix: sys_ \ No newline at end of file diff --git a/ruoyi-generator/target/classes/mapper/generator/GenTableColumnMapper.xml b/ruoyi-generator/target/classes/mapper/generator/GenTableColumnMapper.xml new file mode 100644 index 00000000..3c24013c --- /dev/null +++ b/ruoyi-generator/target/classes/mapper/generator/GenTableColumnMapper.xml @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select column_id, table_id, column_name, column_comment, column_type, java_type, java_field, is_pk, is_increment, is_required, is_insert, is_edit, is_list, is_query, query_type, html_type, dict_type, sort, create_by, create_time, update_by, update_time from gen_table_column + + + + + + + + insert into gen_table_column ( + table_id, + column_name, + column_comment, + column_type, + java_type, + java_field, + is_pk, + is_increment, + is_required, + is_insert, + is_edit, + is_list, + is_query, + query_type, + html_type, + dict_type, + sort, + create_by, + create_time + )values( + #{tableId}, + #{columnName}, + #{columnComment}, + #{columnType}, + #{javaType}, + #{javaField}, + #{isPk}, + #{isIncrement}, + #{isRequired}, + #{isInsert}, + #{isEdit}, + #{isList}, + #{isQuery}, + #{queryType}, + #{htmlType}, + #{dictType}, + #{sort}, + #{createBy}, + sysdate() + ) + + + + update gen_table_column + + column_comment = #{columnComment}, + java_type = #{javaType}, + java_field = #{javaField}, + is_insert = #{isInsert}, + is_edit = #{isEdit}, + is_list = #{isList}, + is_query = #{isQuery}, + is_required = #{isRequired}, + query_type = #{queryType}, + html_type = #{htmlType}, + dict_type = #{dictType}, + sort = #{sort}, + update_by = #{updateBy}, + update_time = sysdate() + + where column_id = #{columnId} + + + + delete from gen_table_column where table_id in + + #{tableId} + + + + + delete from gen_table_column where column_id in + + #{item.columnId} + + + + diff --git a/ruoyi-generator/target/classes/mapper/generator/GenTableMapper.xml b/ruoyi-generator/target/classes/mapper/generator/GenTableMapper.xml new file mode 100644 index 00000000..b605e906 --- /dev/null +++ b/ruoyi-generator/target/classes/mapper/generator/GenTableMapper.xml @@ -0,0 +1,202 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select table_id, table_name, table_comment, sub_table_name, sub_table_fk_name, class_name, tpl_category, package_name, module_name, business_name, function_name, function_author, gen_type, gen_path, options, create_by, create_time, update_by, update_time, remark from gen_table + + + + + + + + + + + + + + + + + + insert into gen_table ( + table_name, + table_comment, + class_name, + tpl_category, + package_name, + module_name, + business_name, + function_name, + function_author, + gen_type, + gen_path, + remark, + create_by, + create_time + )values( + #{tableName}, + #{tableComment}, + #{className}, + #{tplCategory}, + #{packageName}, + #{moduleName}, + #{businessName}, + #{functionName}, + #{functionAuthor}, + #{genType}, + #{genPath}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + update gen_table + + table_name = #{tableName}, + table_comment = #{tableComment}, + sub_table_name = #{subTableName}, + sub_table_fk_name = #{subTableFkName}, + class_name = #{className}, + function_author = #{functionAuthor}, + gen_type = #{genType}, + gen_path = #{genPath}, + tpl_category = #{tplCategory}, + package_name = #{packageName}, + module_name = #{moduleName}, + business_name = #{businessName}, + function_name = #{functionName}, + options = #{options}, + update_by = #{updateBy}, + remark = #{remark}, + update_time = sysdate() + + where table_id = #{tableId} + + + + delete from gen_table where table_id in + + #{tableId} + + + + \ No newline at end of file diff --git a/ruoyi-generator/target/classes/mybatisplusvm/java/controller.java.vm b/ruoyi-generator/target/classes/mybatisplusvm/java/controller.java.vm new file mode 100644 index 00000000..d2894379 --- /dev/null +++ b/ruoyi-generator/target/classes/mybatisplusvm/java/controller.java.vm @@ -0,0 +1,129 @@ +package ${packageName}.controller; + +import java.util.List; + +import io.swagger.annotations.*; +import com.baomidou.mybatisplus.core.metadata.IPage; + +import javax.servlet.http.HttpServletResponse; + +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.BusinessType; +import ${packageName}.domain.${ClassName}; +import ${packageName}.service.I${ClassName}Service; +import com.ruoyi.common.utils.poi.ExcelUtil; +#if($table.crud || $table.sub) +import com.ruoyi.common.core.page.TableDataInfo; +#elseif($table.tree) +#end + +/** + * ${functionName}Controller + * + * @author ${author} + * @date ${datetime} + */ +@RestController +@RequestMapping("/${moduleName}/${businessName}") +@Api(tags = {"【${functionName}】Controller"}) +public class ${ClassName}Controller extends BaseController { + @Autowired + private I${ClassName}Service ${className}Service; + +/** + * 分页查询${functionName}列表 + */ +@PreAuthorize("@ss.hasPermi('${permissionPrefix}:list')") +@GetMapping("/list") +@ApiOperation("分页查询${functionName}列表") + #if($table.crud || $table.sub) + public TableDataInfo list(${ClassName} ${className}) { + IPage<${ClassName}> list = ${className}Service.selectList(getPage(), ${className}); + return getDataTable(list); + } + #elseif($table.tree) + public AjaxResult list(${ClassName} ${className}) { + List<${ClassName}> list = ${className}Service.select${ClassName}List(${className}); + return AjaxResult.success(list); + } + #end + + /** + * 查询${functionName}列表 + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:list')") + @GetMapping("/list/all") + @ApiOperation("查询${functionName}列表") + public AjaxResult listAll(${ClassName} ${className}) { + List<${ClassName}> list = ${className}Service.selectListAll(${className}); + return success(list); + } + + /** + * 导出${functionName}列表 + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:export')") + @Log(title = "${functionName}", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @ApiOperation("导出${functionName}列表Excel") + public void export(HttpServletResponse response, ${ClassName} ${className}) { + List<${ClassName}> list = ${className}Service.selectListAll(${className}); + ExcelUtil<${ClassName}> util = new ExcelUtil<${ClassName}>(${ClassName}. class); + util.exportExcel(response, list, "${functionName}数据"); + } + + /** + * 获取${functionName}详细信息 + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:query')") + @GetMapping(value = "/{${pkColumn.javaField}}") + @ApiOperation("获取${functionName}详细信息") + public AjaxResult getInfo(@PathVariable("${pkColumn.javaField}") ${pkColumn.javaType} ${pkColumn.javaField}) { + return success(${className}Service.selectById(${pkColumn.javaField})); + } + + /** + * 新增${functionName} + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:add')") + @Log(title = "${functionName}", businessType = BusinessType.INSERT) + @PostMapping + @ApiOperation("新增${functionName}") + public AjaxResult add(@RequestBody ${ClassName} ${className}) { + return toAjax(${className}Service.insert(${className})); + } + + /** + * 修改${functionName} + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:edit')") + @Log(title = "${functionName}", businessType = BusinessType.UPDATE) + @PutMapping + @ApiOperation("修改${functionName}") + public AjaxResult edit(@RequestBody ${ClassName} ${className}) { + return toAjax(${className}Service.update(${className})); + } + + /** + * 删除${functionName} + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:remove')") + @Log(title = "${functionName}", businessType = BusinessType.DELETE) + @DeleteMapping("/{${pkColumn.javaField}s}") + @ApiOperation("删除${functionName}") + public AjaxResult remove(@PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s) { + return toAjax(${className}Service.deleteByIds(${pkColumn.javaField}s)); + } +} diff --git a/ruoyi-generator/target/classes/mybatisplusvm/java/domain.java.vm b/ruoyi-generator/target/classes/mybatisplusvm/java/domain.java.vm new file mode 100644 index 00000000..23249f21 --- /dev/null +++ b/ruoyi-generator/target/classes/mybatisplusvm/java/domain.java.vm @@ -0,0 +1,72 @@ +package ${packageName}.domain; + +#foreach ($import in $importList) +import ${import}; +#end +import com.ruoyi.common.annotation.Excel; +#if($table.crud || $table.sub) +import com.ruoyi.common.core.domain.BaseEntity; +#elseif($table.tree) +import com.ruoyi.common.core.domain.TreeEntity; +#end +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import com.baomidou.mybatisplus.annotation.TableName; +/** + * ${functionName}对象 ${tableName} + * + * @author ${author} + * @date ${datetime} + */ +#if($table.crud || $table.sub) + #set($Entity="BaseEntity") +#elseif($table.tree) + #set($Entity="TreeEntity") +#end + +@ApiModel(value = "${ClassName}", description = "${functionName}对象 ${tableName}") +@Data +@TableName(value = "${tableName}", autoResultMap = true) +public class ${ClassName} extends ${Entity} +{ + private static final long serialVersionUID=1L; + +#foreach ($column in $columns) +#if(!$table.isSuperColumn($column.javaField)) + /** $column.columnComment */ +#if($column.list) +#set($parentheseIndex=$column.columnComment.indexOf("(")) +#if($parentheseIndex != -1) +#set($comment=$column.columnComment.substring(0, $parentheseIndex)) +#else +#set($comment=$column.columnComment) +#end +#if($parentheseIndex != -1) + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") +#elseif($column.javaType == 'Date') + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd") +#else + @Excel(name = "${comment}") +#end +#end + @ApiModelProperty(value = "$column.columnComment") + private $column.javaType $column.javaField; + +#end +#end +#if($table.sub) +/** $table.subTable.functionName信息 */ + private List<${subClassName}> ${subclassName}List; + +#end +#foreach ($column in $columns) + #if(!$table.isSuperColumn($column.javaField)) + #if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) + #set($AttrName=$column.javaField) + #else + #set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) + #end + #end#end +} diff --git a/ruoyi-generator/target/classes/mybatisplusvm/java/mapper.java.vm b/ruoyi-generator/target/classes/mybatisplusvm/java/mapper.java.vm new file mode 100644 index 00000000..a46b08e2 --- /dev/null +++ b/ruoyi-generator/target/classes/mybatisplusvm/java/mapper.java.vm @@ -0,0 +1,36 @@ +package ${packageName}.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import java.util.List; +import org.apache.ibatis.annotations.Param; +import ${packageName}.domain.${ClassName}; +#if($table.sub) +import ${packageName}.domain.${subClassName}; +#end + +/** + * ${functionName}Mapper接口 + * + * @author ${author} + * @date ${datetime} + */ +public interface ${ClassName}Mapper extends BaseMapper<${ClassName}> +{ + /** + * 分页查询${functionName}列表 + * + * param page 分页信息 + * @param ${className} ${functionName}信息 + * @return ${functionName}集合 + */ + public IPage<${ClassName}> select${ClassName}Page(IPage<${ClassName}> page,@Param("entity") ${ClassName} ${className}); + + /** + * 查询所有${functionName}列表 + * + * @param ${className} ${functionName}信息 + * @return ${functionName}集合 + */ + public List<${ClassName}> select${ClassName}List(${ClassName} ${className}); +} diff --git a/ruoyi-generator/target/classes/mybatisplusvm/java/service.java.vm b/ruoyi-generator/target/classes/mybatisplusvm/java/service.java.vm new file mode 100644 index 00000000..5cda17db --- /dev/null +++ b/ruoyi-generator/target/classes/mybatisplusvm/java/service.java.vm @@ -0,0 +1,71 @@ +package ${packageName}.service; + +import java.util.List; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import ${packageName}.domain.${ClassName}; + +/** + * ${functionName}Service接口 + * + * @author ${author} + * @date ${datetime} + */ +public interface I${ClassName}Service extends IService<${ClassName}> +{ + /** + * 查询${functionName} + * + * @param ${pkColumn.javaField} ${functionName}ID + * @return ${functionName} + */ + public ${ClassName} selectById(${pkColumn.javaType} ${pkColumn.javaField}); + + /** + * 分页查询${functionName}列表 + * + * @param ${className} ${functionName} + * @return ${functionName}集合 + */ + public IPage<${ClassName}> selectList(IPage<${ClassName}> page, ${ClassName} ${className}); + + /** + * 查询所有${functionName}列表 + * + * @param ${className} ${functionName} + * @return ${functionName}集合 + */ + public List<${ClassName}> selectListAll(${ClassName} ${className}); + + /** + * 新增${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ + public int insert(${ClassName} ${className}); + + /** + * 修改${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ + public int update(${ClassName} ${className}); + + /** + * 批量删除${functionName} + * + * @param ${pkColumn.javaField}s 需要删除的${functionName}ID + * @return 结果 + */ + public int deleteByIds(${pkColumn.javaType}[] ${pkColumn.javaField}s); + + /** + * 删除${functionName}信息 + * + * @param ${pkColumn.javaField} ${functionName}ID + * @return 结果 + */ + public int deleteById(${pkColumn.javaType} ${pkColumn.javaField}); +} diff --git a/ruoyi-generator/target/classes/mybatisplusvm/java/serviceImpl.java.vm b/ruoyi-generator/target/classes/mybatisplusvm/java/serviceImpl.java.vm new file mode 100644 index 00000000..77411bd9 --- /dev/null +++ b/ruoyi-generator/target/classes/mybatisplusvm/java/serviceImpl.java.vm @@ -0,0 +1,174 @@ +package ${packageName}.service.impl; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import java.util.Arrays; +import java.util.List; +#foreach ($column in $columns) +#if($column.javaField == 'createTime' || $column.javaField == 'updateTime') +import com.ruoyi.common.utils.DateUtils; +#break +#end +#end +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +#if($table.sub) +import java.util.ArrayList; +import com.ruoyi.common.utils.StringUtils; +import org.springframework.transaction.annotation.Transactional; +import ${packageName}.domain.${subClassName}; +#end +import ${packageName}.mapper.${ClassName}Mapper; +import ${packageName}.domain.${ClassName}; +import ${packageName}.service.I${ClassName}Service; + +/** + * ${functionName}Service业务层处理 + * + * @author ${author} + * @date ${datetime} + */ +@Service +public class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}> implements I${ClassName}Service +{ + @Autowired + private ${ClassName}Mapper ${className}Mapper; + + /** + * 查询${functionName} + * + * @param ${pkColumn.javaField} ${functionName}ID + * @return ${functionName} + */ + @Override + public ${ClassName} selectById(${pkColumn.javaType} ${pkColumn.javaField}) { + return ${className}Mapper.selectById(${pkColumn.javaField}); + } + + /** + * 分页查询${functionName}列表 + * + * @param ${className} ${functionName} + * @return ${functionName} + */ + @Override + public IPage<${ClassName}> selectList(IPage<${ClassName}> page, ${ClassName} ${className}) { + return ${className}Mapper.select${ClassName}Page(page, ${className}); + } + + /** + * 查询所有${functionName}列表 + * + * @param ${className} ${functionName} + * @return ${functionName} + */ + @Override + public List<${ClassName}> selectListAll(${ClassName} ${className}) { + return ${className}Mapper.select${ClassName}List(${className}); + } + + /** + * 新增${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int insert(${ClassName} ${className}) { +#foreach ($column in $columns) + #if($column.javaField == 'createTime') + ${className}.setCreateTime(DateUtils.getNowDate()); + #end +#end +#if($table.sub) + int rows = ${className}Mapper.insert(${className}); + insert${subClassName}(${className}); + return rows; +#else + return ${className}Mapper.insert(${className}); +#end + } + + /** + * 修改${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int update(${ClassName} ${className}) { +#foreach ($column in $columns) + #if($column.javaField == 'updateTime') + ${className}.setUpdateTime(DateUtils.getNowDate()); + #end +#end +#if($table.sub) + ${className}Mapper.delete${subClassName}By${subTableFkClassName}(${className}.get${pkColumn.capJavaField}()); + insert${subClassName}(${className}); +#end + return ${className}Mapper.updateById(${className}); + } + + /** + * 批量删除${functionName} + * + * @param ${pkColumn.javaField}s 需要删除的${functionName}ID + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int deleteByIds(${pkColumn.javaType}[] ${pkColumn.javaField}s) { +#if($table.sub) + ${className}Mapper.delete${subClassName}By${subTableFkClassName}s(${pkColumn.javaField}s); +#end + return ${className}Mapper.deleteBatchIds(Arrays.asList(${pkColumn.javaField}s)); + } + + /** + * 删除${functionName}信息 + * + * @param ${pkColumn.javaField} ${functionName}ID + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int deleteById(${pkColumn.javaType} ${pkColumn.javaField}) { +#if($table.sub) + ${className}Mapper.delete${subClassName}By${subTableFkClassName}(${pkColumn.javaField}); +#end + return ${className}Mapper.deleteById(${pkColumn.javaField}); + } +#if($table.sub) + + /** + * 新增${subTable.functionName}信息 + * + * @param ${className} ${functionName}对象 + */ + public void insert${subClassName}(${ClassName} ${className}) { + List<${subClassName}> ${subclassName}List = ${className}.get${subClassName}List(); + Long ${pkColumn.javaField} = ${className}.get${pkColumn.capJavaField}(); + if (StringUtils.isNotNull(${subclassName}List)) { + List<${subClassName}> list = new ArrayList<${subClassName}>(); + for (${subClassName} ${subclassName} :${subclassName}List) + { + ${subclassName}.set${subTableFkClassName}(${pkColumn.javaField}); + list.add(${subclassName}); + } + if (list.size() > 0) { + ${className}Mapper.batch${subClassName}(list); + } + } + } +#end +} diff --git a/ruoyi-generator/target/classes/mybatisplusvm/java/sub-domain.java.vm b/ruoyi-generator/target/classes/mybatisplusvm/java/sub-domain.java.vm new file mode 100644 index 00000000..ad3890a0 --- /dev/null +++ b/ruoyi-generator/target/classes/mybatisplusvm/java/sub-domain.java.vm @@ -0,0 +1,76 @@ +package ${packageName}.domain; + + #foreach ($import in $subImportList) + import ${import}; + #end +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * ${subTable.functionName}对象 ${subTableName} + * + * @author ${author} + * @date ${datetime} + */ +public class ${subClassName} extends BaseEntity + { +private static final long serialVersionUID=1L; + +#foreach ($column in $subTable.columns) + #if(!$table.isSuperColumn($column.javaField)) + /** $column.columnComment */ + #if($column.list) + #set($parentheseIndex=$column.columnComment.indexOf("(")) + #if($parentheseIndex != -1) + #set($comment=$column.columnComment.substring(0, $parentheseIndex)) + #else + #set($comment=$column.columnComment) + #end + #if($parentheseIndex != -1) + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") + #elseif($column.javaType == 'Date') + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd") + #else + @Excel(name = "${comment}") + #end + #end + private $column.javaType $column.javaField; + + #end +#end +#foreach ($column in $subTable.columns) + #if(!$table.isSuperColumn($column.javaField)) + #if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) + #set($AttrName=$column.javaField) + #else + #set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) + #end + public void set${AttrName}($column.javaType $column.javaField) + { + this.$column.javaField = $column.javaField; + } + + public $column.javaType get${AttrName}() + { + return $column.javaField; + } + #end +#end + +@Override +public String toString(){ + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + #foreach ($column in $subTable.columns) + #if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) + #set($AttrName=$column.javaField) + #else + #set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) + #end + .append("${column.javaField}",get${AttrName}()) + #end + .toString(); + } + } diff --git a/ruoyi-generator/target/classes/mybatisplusvm/js/api.js.vm b/ruoyi-generator/target/classes/mybatisplusvm/js/api.js.vm new file mode 100644 index 00000000..9295524a --- /dev/null +++ b/ruoyi-generator/target/classes/mybatisplusvm/js/api.js.vm @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询${functionName}列表 +export function list${BusinessName}(query) { + return request({ + url: '/${moduleName}/${businessName}/list', + method: 'get', + params: query + }) +} + +// 查询${functionName}详细 +export function get${BusinessName}(${pkColumn.javaField}) { + return request({ + url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField}, + method: 'get' + }) +} + +// 新增${functionName} +export function add${BusinessName}(data) { + return request({ + url: '/${moduleName}/${businessName}', + method: 'post', + data: data + }) +} + +// 修改${functionName} +export function update${BusinessName}(data) { + return request({ + url: '/${moduleName}/${businessName}', + method: 'put', + data: data + }) +} + +// 删除${functionName} +export function del${BusinessName}(${pkColumn.javaField}) { + return request({ + url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField}, + method: 'delete' + }) +} diff --git a/ruoyi-generator/target/classes/mybatisplusvm/sql/sql.vm b/ruoyi-generator/target/classes/mybatisplusvm/sql/sql.vm new file mode 100644 index 00000000..05755835 --- /dev/null +++ b/ruoyi-generator/target/classes/mybatisplusvm/sql/sql.vm @@ -0,0 +1,22 @@ +-- 菜单 SQL +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 'admin', sysdate(), '', null, '${functionName}菜单'); + +-- 按钮父菜单ID +SELECT @parentId := LAST_INSERT_ID(); + +-- 按钮 SQL +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}查询', @parentId, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}新增', @parentId, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}修改', @parentId, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}删除', @parentId, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}导出', @parentId, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 'admin', sysdate(), '', null, ''); \ No newline at end of file diff --git a/ruoyi-generator/target/classes/mybatisplusvm/vue/index-tree.vue.vm b/ruoyi-generator/target/classes/mybatisplusvm/vue/index-tree.vue.vm new file mode 100644 index 00000000..3def7045 --- /dev/null +++ b/ruoyi-generator/target/classes/mybatisplusvm/vue/index-tree.vue.vm @@ -0,0 +1,502 @@ + + + diff --git a/ruoyi-generator/target/classes/mybatisplusvm/vue/index.vue.vm b/ruoyi-generator/target/classes/mybatisplusvm/vue/index.vue.vm new file mode 100644 index 00000000..e9a1fae1 --- /dev/null +++ b/ruoyi-generator/target/classes/mybatisplusvm/vue/index.vue.vm @@ -0,0 +1,598 @@ + + + diff --git a/ruoyi-generator/target/classes/mybatisplusvm/vue/v3/index-tree.vue.vm b/ruoyi-generator/target/classes/mybatisplusvm/vue/v3/index-tree.vue.vm new file mode 100644 index 00000000..862297c7 --- /dev/null +++ b/ruoyi-generator/target/classes/mybatisplusvm/vue/v3/index-tree.vue.vm @@ -0,0 +1,486 @@ + + + diff --git a/ruoyi-generator/target/classes/mybatisplusvm/vue/v3/index.vue.vm b/ruoyi-generator/target/classes/mybatisplusvm/vue/v3/index.vue.vm new file mode 100644 index 00000000..f66cc3b8 --- /dev/null +++ b/ruoyi-generator/target/classes/mybatisplusvm/vue/v3/index.vue.vm @@ -0,0 +1,596 @@ + + + diff --git a/ruoyi-generator/target/classes/mybatisplusvm/vue/v3/readme.txt b/ruoyi-generator/target/classes/mybatisplusvm/vue/v3/readme.txt new file mode 100644 index 00000000..99239bb5 --- /dev/null +++ b/ruoyi-generator/target/classes/mybatisplusvm/vue/v3/readme.txt @@ -0,0 +1 @@ +ʹõRuoYi-Vue3ǰˣôҪһ´Ŀ¼ģindex.vue.vmindex-tree.vue.vmļϼvueĿ¼ \ No newline at end of file diff --git a/ruoyi-generator/target/classes/mybatisplusvm/xml/mapper.xml.vm b/ruoyi-generator/target/classes/mybatisplusvm/xml/mapper.xml.vm new file mode 100644 index 00000000..4fc7d573 --- /dev/null +++ b/ruoyi-generator/target/classes/mybatisplusvm/xml/mapper.xml.vm @@ -0,0 +1,109 @@ + + + + + +#foreach ($column in $columns) + +#end + +#if($table.sub) + + + + + + +#foreach ($column in $subTable.columns) + +#end + +#end + + + select#foreach($column in $columns) $column.columnName#if($foreach.count != $columns.size()),#end#end from ${tableName} + + + + + + diff --git a/ruoyi-generator/target/classes/vm/java/controller.java.vm b/ruoyi-generator/target/classes/vm/java/controller.java.vm new file mode 100644 index 00000000..5955c6f6 --- /dev/null +++ b/ruoyi-generator/target/classes/vm/java/controller.java.vm @@ -0,0 +1,108 @@ +package ${packageName}.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.BusinessType; +import ${packageName}.domain.${ClassName}; +import ${packageName}.service.I${ClassName}Service; +import com.ruoyi.common.utils.poi.ExcelUtil; +#if($table.crud || $table.sub) +import com.ruoyi.common.core.page.TableDataInfo; +#elseif($table.tree) +#end + +/** + * ${functionName}Controller + * + * @author ${author} + * @date ${datetime} + */ +@RestController +@RequestMapping("/${moduleName}/${businessName}") +public class ${ClassName}Controller extends BaseController { + @Autowired + private I${ClassName}Service ${className}Service; + +/** + * 查询${functionName}列表 + */ +@PreAuthorize("@ss.hasPermi('${permissionPrefix}:list')") +@GetMapping("/list") + #if($table.crud || $table.sub) + public TableDataInfo list(${ClassName} ${className}) { + startPage(); + List<${ClassName}> list = ${className}Service.select${ClassName}List(${className}); + return getDataTable(list); + } + #elseif($table.tree) + public AjaxResult list(${ClassName} ${className}) { + List<${ClassName}> list = ${className}Service.select${ClassName}List(${className}); + return success(list); + } + #end + + /** + * 导出${functionName}列表 + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:export')") + @Log(title = "${functionName}", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, ${ClassName} ${className}) { + List<${ClassName}> list = ${className}Service.select${ClassName}List(${className}); + ExcelUtil<${ClassName}> util = new ExcelUtil<${ClassName}>(${ClassName}. class); + util.exportExcel(response, list, "${functionName}数据"); + } + + /** + * 获取${functionName}详细信息 + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:query')") + @GetMapping(value = "/{${pkColumn.javaField}}") + public AjaxResult getInfo(@PathVariable("${pkColumn.javaField}") ${pkColumn.javaType} ${pkColumn.javaField}) { + return success(${className}Service.select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField})); + } + + /** + * 新增${functionName} + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:add')") + @Log(title = "${functionName}", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody ${ClassName} ${className}) { + return toAjax(${className}Service.insert${ClassName}(${className})); + } + + /** + * 修改${functionName} + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:edit')") + @Log(title = "${functionName}", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody ${ClassName} ${className}) { + return toAjax(${className}Service.update${ClassName}(${className})); + } + + /** + * 删除${functionName} + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:remove')") + @Log(title = "${functionName}", businessType = BusinessType.DELETE) + @DeleteMapping("/{${pkColumn.javaField}s}") + public AjaxResult remove(@PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s) { + return toAjax(${className}Service.delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaField}s)); + } +} diff --git a/ruoyi-generator/target/classes/vm/java/domain.java.vm b/ruoyi-generator/target/classes/vm/java/domain.java.vm new file mode 100644 index 00000000..bd51c177 --- /dev/null +++ b/ruoyi-generator/target/classes/vm/java/domain.java.vm @@ -0,0 +1,105 @@ +package ${packageName}.domain; + +#foreach ($import in $importList) +import ${import}; +#end +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +#if($table.crud || $table.sub) +import com.ruoyi.common.core.domain.BaseEntity; +#elseif($table.tree) +import com.ruoyi.common.core.domain.TreeEntity; +#end + +/** + * ${functionName}对象 ${tableName} + * + * @author ${author} + * @date ${datetime} + */ +#if($table.crud || $table.sub) +#set($Entity="BaseEntity") +#elseif($table.tree) +#set($Entity="TreeEntity") +#end +public class ${ClassName} extends ${Entity} +{ + private static final long serialVersionUID = 1L; + +#foreach ($column in $columns) +#if(!$table.isSuperColumn($column.javaField)) + /** $column.columnComment */ +#if($column.list) +#set($parentheseIndex=$column.columnComment.indexOf("(")) +#if($parentheseIndex != -1) +#set($comment=$column.columnComment.substring(0, $parentheseIndex)) +#else +#set($comment=$column.columnComment) +#end +#if($parentheseIndex != -1) + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") +#elseif($column.javaType == 'Date') + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd") +#else + @Excel(name = "${comment}") +#end +#end + private $column.javaType $column.javaField; + +#end +#end +#if($table.sub) + /** $table.subTable.functionName信息 */ + private List<${subClassName}> ${subclassName}List; + +#end +#foreach ($column in $columns) +#if(!$table.isSuperColumn($column.javaField)) +#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) +#set($AttrName=$column.javaField) +#else +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#end + public void set${AttrName}($column.javaType $column.javaField) + { + this.$column.javaField = $column.javaField; + } + + public $column.javaType get${AttrName}() + { + return $column.javaField; + } +#end +#end + +#if($table.sub) + public List<${subClassName}> get${subClassName}List() + { + return ${subclassName}List; + } + + public void set${subClassName}List(List<${subClassName}> ${subclassName}List) + { + this.${subclassName}List = ${subclassName}List; + } + +#end + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) +#foreach ($column in $columns) +#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) +#set($AttrName=$column.javaField) +#else +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#end + .append("${column.javaField}", get${AttrName}()) +#end +#if($table.sub) + .append("${subclassName}List", get${subClassName}List()) +#end + .toString(); + } +} diff --git a/ruoyi-generator/target/classes/vm/java/mapper.java.vm b/ruoyi-generator/target/classes/vm/java/mapper.java.vm new file mode 100644 index 00000000..7e7d7c26 --- /dev/null +++ b/ruoyi-generator/target/classes/vm/java/mapper.java.vm @@ -0,0 +1,91 @@ +package ${packageName}.mapper; + +import java.util.List; +import ${packageName}.domain.${ClassName}; +#if($table.sub) +import ${packageName}.domain.${subClassName}; +#end + +/** + * ${functionName}Mapper接口 + * + * @author ${author} + * @date ${datetime} + */ +public interface ${ClassName}Mapper +{ + /** + * 查询${functionName} + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return ${functionName} + */ + public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); + + /** + * 查询${functionName}列表 + * + * @param ${className} ${functionName} + * @return ${functionName}集合 + */ + public List<${ClassName}> select${ClassName}List(${ClassName} ${className}); + + /** + * 新增${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ + public int insert${ClassName}(${ClassName} ${className}); + + /** + * 修改${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ + public int update${ClassName}(${ClassName} ${className}); + + /** + * 删除${functionName} + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return 结果 + */ + public int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); + + /** + * 批量删除${functionName} + * + * @param ${pkColumn.javaField}s 需要删除的数据主键集合 + * @return 结果 + */ + public int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s); +#if($table.sub) + + /** + * 批量删除${subTable.functionName} + * + * @param ${pkColumn.javaField}s 需要删除的数据主键集合 + * @return 结果 + */ + public int delete${subClassName}By${subTableFkClassName}s(${pkColumn.javaType}[] ${pkColumn.javaField}s); + + /** + * 批量新增${subTable.functionName} + * + * @param ${subclassName}List ${subTable.functionName}列表 + * @return 结果 + */ + public int batch${subClassName}(List<${subClassName}> ${subclassName}List); + + + /** + * 通过${functionName}主键删除${subTable.functionName}信息 + * + * @param ${pkColumn.javaField} ${functionName}ID + * @return 结果 + */ + public int delete${subClassName}By${subTableFkClassName}(${pkColumn.javaType} ${pkColumn.javaField}); +#end +} diff --git a/ruoyi-generator/target/classes/vm/java/service.java.vm b/ruoyi-generator/target/classes/vm/java/service.java.vm new file mode 100644 index 00000000..264882b2 --- /dev/null +++ b/ruoyi-generator/target/classes/vm/java/service.java.vm @@ -0,0 +1,61 @@ +package ${packageName}.service; + +import java.util.List; +import ${packageName}.domain.${ClassName}; + +/** + * ${functionName}Service接口 + * + * @author ${author} + * @date ${datetime} + */ +public interface I${ClassName}Service +{ + /** + * 查询${functionName} + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return ${functionName} + */ + public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); + + /** + * 查询${functionName}列表 + * + * @param ${className} ${functionName} + * @return ${functionName}集合 + */ + public List<${ClassName}> select${ClassName}List(${ClassName} ${className}); + + /** + * 新增${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ + public int insert${ClassName}(${ClassName} ${className}); + + /** + * 修改${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ + public int update${ClassName}(${ClassName} ${className}); + + /** + * 批量删除${functionName} + * + * @param ${pkColumn.javaField}s 需要删除的${functionName}主键集合 + * @return 结果 + */ + public int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s); + + /** + * 删除${functionName}信息 + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return 结果 + */ + public int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); +} diff --git a/ruoyi-generator/target/classes/vm/java/serviceImpl.java.vm b/ruoyi-generator/target/classes/vm/java/serviceImpl.java.vm new file mode 100644 index 00000000..14746e1a --- /dev/null +++ b/ruoyi-generator/target/classes/vm/java/serviceImpl.java.vm @@ -0,0 +1,169 @@ +package ${packageName}.service.impl; + +import java.util.List; +#foreach ($column in $columns) +#if($column.javaField == 'createTime' || $column.javaField == 'updateTime') +import com.ruoyi.common.utils.DateUtils; +#break +#end +#end +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +#if($table.sub) +import java.util.ArrayList; +import com.ruoyi.common.utils.StringUtils; +import org.springframework.transaction.annotation.Transactional; +import ${packageName}.domain.${subClassName}; +#end +import ${packageName}.mapper.${ClassName}Mapper; +import ${packageName}.domain.${ClassName}; +import ${packageName}.service.I${ClassName}Service; + +/** + * ${functionName}Service业务层处理 + * + * @author ${author} + * @date ${datetime} + */ +@Service +public class ${ClassName}ServiceImpl implements I${ClassName}Service +{ + @Autowired + private ${ClassName}Mapper ${className}Mapper; + + /** + * 查询${functionName} + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return ${functionName} + */ + @Override + public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}) + { + return ${className}Mapper.select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField}); + } + + /** + * 查询${functionName}列表 + * + * @param ${className} ${functionName} + * @return ${functionName} + */ + @Override + public List<${ClassName}> select${ClassName}List(${ClassName} ${className}) + { + return ${className}Mapper.select${ClassName}List(${className}); + } + + /** + * 新增${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int insert${ClassName}(${ClassName} ${className}) + { +#foreach ($column in $columns) +#if($column.javaField == 'createTime') + ${className}.setCreateTime(DateUtils.getNowDate()); +#end +#end +#if($table.sub) + int rows = ${className}Mapper.insert${ClassName}(${className}); + insert${subClassName}(${className}); + return rows; +#else + return ${className}Mapper.insert${ClassName}(${className}); +#end + } + + /** + * 修改${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int update${ClassName}(${ClassName} ${className}) + { +#foreach ($column in $columns) +#if($column.javaField == 'updateTime') + ${className}.setUpdateTime(DateUtils.getNowDate()); +#end +#end +#if($table.sub) + ${className}Mapper.delete${subClassName}By${subTableFkClassName}(${className}.get${pkColumn.capJavaField}()); + insert${subClassName}(${className}); +#end + return ${className}Mapper.update${ClassName}(${className}); + } + + /** + * 批量删除${functionName} + * + * @param ${pkColumn.javaField}s 需要删除的${functionName}主键 + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s) + { +#if($table.sub) + ${className}Mapper.delete${subClassName}By${subTableFkClassName}s(${pkColumn.javaField}s); +#end + return ${className}Mapper.delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaField}s); + } + + /** + * 删除${functionName}信息 + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}) + { +#if($table.sub) + ${className}Mapper.delete${subClassName}By${subTableFkClassName}(${pkColumn.javaField}); +#end + return ${className}Mapper.delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField}); + } +#if($table.sub) + + /** + * 新增${subTable.functionName}信息 + * + * @param ${className} ${functionName}对象 + */ + public void insert${subClassName}(${ClassName} ${className}) + { + List<${subClassName}> ${subclassName}List = ${className}.get${subClassName}List(); + ${pkColumn.javaType} ${pkColumn.javaField} = ${className}.get${pkColumn.capJavaField}(); + if (StringUtils.isNotNull(${subclassName}List)) + { + List<${subClassName}> list = new ArrayList<${subClassName}>(); + for (${subClassName} ${subclassName} : ${subclassName}List) + { + ${subclassName}.set${subTableFkClassName}(${pkColumn.javaField}); + list.add(${subclassName}); + } + if (list.size() > 0) + { + ${className}Mapper.batch${subClassName}(list); + } + } + } +#end +} diff --git a/ruoyi-generator/target/classes/vm/java/sub-domain.java.vm b/ruoyi-generator/target/classes/vm/java/sub-domain.java.vm new file mode 100644 index 00000000..a3f53eba --- /dev/null +++ b/ruoyi-generator/target/classes/vm/java/sub-domain.java.vm @@ -0,0 +1,76 @@ +package ${packageName}.domain; + +#foreach ($import in $subImportList) +import ${import}; +#end +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * ${subTable.functionName}对象 ${subTableName} + * + * @author ${author} + * @date ${datetime} + */ +public class ${subClassName} extends BaseEntity +{ + private static final long serialVersionUID = 1L; + +#foreach ($column in $subTable.columns) +#if(!$table.isSuperColumn($column.javaField)) + /** $column.columnComment */ +#if($column.list) +#set($parentheseIndex=$column.columnComment.indexOf("(")) +#if($parentheseIndex != -1) +#set($comment=$column.columnComment.substring(0, $parentheseIndex)) +#else +#set($comment=$column.columnComment) +#end +#if($parentheseIndex != -1) + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") +#elseif($column.javaType == 'Date') + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd") +#else + @Excel(name = "${comment}") +#end +#end + private $column.javaType $column.javaField; + +#end +#end +#foreach ($column in $subTable.columns) +#if(!$table.isSuperColumn($column.javaField)) +#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) +#set($AttrName=$column.javaField) +#else +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#end + public void set${AttrName}($column.javaType $column.javaField) + { + this.$column.javaField = $column.javaField; + } + + public $column.javaType get${AttrName}() + { + return $column.javaField; + } +#end +#end + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) +#foreach ($column in $subTable.columns) +#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) +#set($AttrName=$column.javaField) +#else +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#end + .append("${column.javaField}", get${AttrName}()) +#end + .toString(); + } +} diff --git a/ruoyi-generator/target/classes/vm/js/api.js.vm b/ruoyi-generator/target/classes/vm/js/api.js.vm new file mode 100644 index 00000000..9295524a --- /dev/null +++ b/ruoyi-generator/target/classes/vm/js/api.js.vm @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询${functionName}列表 +export function list${BusinessName}(query) { + return request({ + url: '/${moduleName}/${businessName}/list', + method: 'get', + params: query + }) +} + +// 查询${functionName}详细 +export function get${BusinessName}(${pkColumn.javaField}) { + return request({ + url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField}, + method: 'get' + }) +} + +// 新增${functionName} +export function add${BusinessName}(data) { + return request({ + url: '/${moduleName}/${businessName}', + method: 'post', + data: data + }) +} + +// 修改${functionName} +export function update${BusinessName}(data) { + return request({ + url: '/${moduleName}/${businessName}', + method: 'put', + data: data + }) +} + +// 删除${functionName} +export function del${BusinessName}(${pkColumn.javaField}) { + return request({ + url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField}, + method: 'delete' + }) +} diff --git a/ruoyi-generator/target/classes/vm/sql/sql.vm b/ruoyi-generator/target/classes/vm/sql/sql.vm new file mode 100644 index 00000000..05755835 --- /dev/null +++ b/ruoyi-generator/target/classes/vm/sql/sql.vm @@ -0,0 +1,22 @@ +-- 菜单 SQL +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 'admin', sysdate(), '', null, '${functionName}菜单'); + +-- 按钮父菜单ID +SELECT @parentId := LAST_INSERT_ID(); + +-- 按钮 SQL +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}查询', @parentId, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}新增', @parentId, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}修改', @parentId, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}删除', @parentId, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}导出', @parentId, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 'admin', sysdate(), '', null, ''); \ No newline at end of file diff --git a/ruoyi-generator/target/classes/vm/vue/index-tree.vue.vm b/ruoyi-generator/target/classes/vm/vue/index-tree.vue.vm new file mode 100644 index 00000000..3def7045 --- /dev/null +++ b/ruoyi-generator/target/classes/vm/vue/index-tree.vue.vm @@ -0,0 +1,502 @@ + + + diff --git a/ruoyi-generator/target/classes/vm/vue/index.vue.vm b/ruoyi-generator/target/classes/vm/vue/index.vue.vm new file mode 100644 index 00000000..3f9a4b4a --- /dev/null +++ b/ruoyi-generator/target/classes/vm/vue/index.vue.vm @@ -0,0 +1,609 @@ + + + diff --git a/ruoyi-generator/target/classes/vm/vue/v3/index-tree.vue.vm b/ruoyi-generator/target/classes/vm/vue/v3/index-tree.vue.vm new file mode 100644 index 00000000..862297c7 --- /dev/null +++ b/ruoyi-generator/target/classes/vm/vue/v3/index-tree.vue.vm @@ -0,0 +1,486 @@ + + + diff --git a/ruoyi-generator/target/classes/vm/vue/v3/index.vue.vm b/ruoyi-generator/target/classes/vm/vue/v3/index.vue.vm new file mode 100644 index 00000000..f66cc3b8 --- /dev/null +++ b/ruoyi-generator/target/classes/vm/vue/v3/index.vue.vm @@ -0,0 +1,596 @@ + + + diff --git a/ruoyi-generator/target/classes/vm/vue/v3/readme.txt b/ruoyi-generator/target/classes/vm/vue/v3/readme.txt new file mode 100644 index 00000000..99239bb5 --- /dev/null +++ b/ruoyi-generator/target/classes/vm/vue/v3/readme.txt @@ -0,0 +1 @@ +ʹõRuoYi-Vue3ǰˣôҪһ´Ŀ¼ģindex.vue.vmindex-tree.vue.vmļϼvueĿ¼ \ No newline at end of file diff --git a/ruoyi-generator/target/classes/vm/xml/mapper.xml.vm b/ruoyi-generator/target/classes/vm/xml/mapper.xml.vm new file mode 100644 index 00000000..0ceb3d85 --- /dev/null +++ b/ruoyi-generator/target/classes/vm/xml/mapper.xml.vm @@ -0,0 +1,135 @@ + + + + + +#foreach ($column in $columns) + +#end + +#if($table.sub) + + + + + + +#foreach ($column in $subTable.columns) + +#end + +#end + + + select#foreach($column in $columns) $column.columnName#if($foreach.count != $columns.size()),#end#end from ${tableName} + + + + + + + + insert into ${tableName} + +#foreach($column in $columns) +#if($column.columnName != $pkColumn.columnName || !$pkColumn.increment) + $column.columnName, +#end +#end + + +#foreach($column in $columns) +#if($column.columnName != $pkColumn.columnName || !$pkColumn.increment) + #{$column.javaField}, +#end +#end + + + + + update ${tableName} + +#foreach($column in $columns) +#if($column.columnName != $pkColumn.columnName) + $column.columnName = #{$column.javaField}, +#end +#end + + where ${pkColumn.columnName} = #{${pkColumn.javaField}} + + + + delete from ${tableName} where ${pkColumn.columnName} = #{${pkColumn.javaField}} + + + + delete from ${tableName} where ${pkColumn.columnName} in + + #{${pkColumn.javaField}} + + +#if($table.sub) + + + delete from ${subTableName} where ${subTableFkName} in + + #{${subTableFkclassName}} + + + + + delete from ${subTableName} where ${subTableFkName} = #{${subTableFkclassName}} + + + + insert into ${subTableName}(#foreach($column in $subTable.columns) $column.columnName#if($foreach.count != $subTable.columns.size()),#end#end) values + + (#foreach($column in $subTable.columns) #{item.$column.javaField}#if($foreach.count != $subTable.columns.size()),#end#end) + + +#end + \ No newline at end of file diff --git a/ruoyi-plugin/pom.xml b/ruoyi-plugin/pom.xml new file mode 100644 index 00000000..478cc7fb --- /dev/null +++ b/ruoyi-plugin/pom.xml @@ -0,0 +1,20 @@ + + + + ruoyi + com.ruoyi + 3.8.4 + + 4.0.0 + + ruoyi-plugin + pom + + ruoyi-springboot-starter-tenant + ruoyi-springboot-starter-oss + + + + diff --git a/ruoyi-plugin/ruoyi-springboot-starter-oss/pom.xml b/ruoyi-plugin/ruoyi-springboot-starter-oss/pom.xml new file mode 100644 index 00000000..57a0dfc4 --- /dev/null +++ b/ruoyi-plugin/ruoyi-springboot-starter-oss/pom.xml @@ -0,0 +1,34 @@ + + + + ruoyi-plugin + com.ruoyi + 3.8.4 + + 4.0.0 + + ruoyi-springboot-starter-oss + + + + org.springframework.boot + spring-boot-starter-web + provided + + + + + org.projectlombok + lombok + true + + + + com.amazonaws + aws-java-sdk-s3 + + + + diff --git a/ruoyi-plugin/ruoyi-springboot-starter-oss/src/main/java/com/ruoyi/plugin/oss/config/OssConfiguration.java b/ruoyi-plugin/ruoyi-springboot-starter-oss/src/main/java/com/ruoyi/plugin/oss/config/OssConfiguration.java new file mode 100644 index 00000000..ccab10af --- /dev/null +++ b/ruoyi-plugin/ruoyi-springboot-starter-oss/src/main/java/com/ruoyi/plugin/oss/config/OssConfiguration.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2018-2025, lengleng All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * Neither the name of the pig4cloud.com developer nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * Author: lengleng (wangiegie@gmail.com) + */ +package com.ruoyi.plugin.oss.config; + +import com.ruoyi.plugin.oss.controller.OssEndpoint; +import com.ruoyi.plugin.oss.core.OssTemplate; +import com.ruoyi.plugin.oss.props.OssProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * AWS自动配置类 + * + * @author lengleng + * @author 858695266 + * @link https://github.com/pig-mesh/oss-spring-boot-starter + * @since 1.0.0 + */ +@Configuration(proxyBeanMethods = false) +@EnableConfigurationProperties({OssProperties.class}) +public class OssConfiguration { + + @Autowired + private OssProperties properties; + + /** + * OSS操作模板 + * + * @return OSS操作模板 + */ + @Bean + @ConditionalOnMissingBean(OssTemplate.class) + @ConditionalOnProperty(prefix = OssProperties.PREFIX, name = "enable", havingValue = "true", matchIfMissing = true) + public OssTemplate ossTemplate(OssProperties properties) { + return new OssTemplate(properties); + } + + /** + * OSS端点信息 + * + * @param template oss操作模版 + * @return oss远程服务端点 + */ + @Bean + @ConditionalOnWebApplication + @ConditionalOnProperty(prefix = OssProperties.PREFIX, name = "http.enable", havingValue = "true") + public OssEndpoint ossEndpoint(OssTemplate template) { + return new OssEndpoint(template); + } +} diff --git a/ruoyi-plugin/ruoyi-springboot-starter-oss/src/main/java/com/ruoyi/plugin/oss/controller/OssEndpoint.java b/ruoyi-plugin/ruoyi-springboot-starter-oss/src/main/java/com/ruoyi/plugin/oss/controller/OssEndpoint.java new file mode 100644 index 00000000..0d67206b --- /dev/null +++ b/ruoyi-plugin/ruoyi-springboot-starter-oss/src/main/java/com/ruoyi/plugin/oss/controller/OssEndpoint.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2018-2025, lengleng All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * Neither the name of the pig4cloud.com developer nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * Author: lengleng (wangiegie@gmail.com) + */ +package com.ruoyi.plugin.oss.controller; + +import com.amazonaws.services.s3.model.Bucket; +import com.amazonaws.services.s3.model.S3Object; +import com.amazonaws.services.s3.model.S3ObjectSummary; +import com.ruoyi.plugin.oss.core.OssTemplate; +import lombok.AllArgsConstructor; +import lombok.SneakyThrows; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * AWS对外提供服务接口 + * + * @author lengleng + * @author 858695266 + * @since 1.0.0 + */ +@RestController +@AllArgsConstructor +@RequestMapping("/oss") +public class OssEndpoint { + + private final OssTemplate template; + + /** + * Bucket Endpoints + */ + @SneakyThrows + @PostMapping("/bucket/{bucketName}") + public Bucket createBucket(@PathVariable String bucketName) { + + template.createBucket(bucketName); + return template.getBucket(bucketName).get(); + + } + + @SneakyThrows + @GetMapping("/bucket") + public List getBuckets() { + return template.getAllBuckets(); + } + + @SneakyThrows + @GetMapping("/bucket/{bucketName}") + public Bucket getBucket(@PathVariable String bucketName) { + return template.getBucket(bucketName).orElseThrow(() -> new IllegalArgumentException("Bucket Name not found!")); + } + + @SneakyThrows + @DeleteMapping("/bucket/{bucketName}") + @ResponseStatus(HttpStatus.ACCEPTED) + public void deleteBucket(@PathVariable String bucketName) { + template.removeBucket(bucketName); + } + + /** + * Object Endpoints + */ + @SneakyThrows + @PostMapping("/object/{bucketName}") + public S3Object createObject(@RequestBody MultipartFile object, @PathVariable String bucketName) { + String name = object.getOriginalFilename(); + template.putObject(bucketName, name, object.getInputStream(), object.getSize(), object.getContentType()); + return template.getObjectInfo(bucketName, name); + + } + + @SneakyThrows + @PostMapping("/object/{bucketName}/{objectName}") + public S3Object createObject(@RequestBody MultipartFile object, @PathVariable String bucketName, + @PathVariable String objectName) { + template.putObject(bucketName, objectName, object.getInputStream(), object.getSize(), object.getContentType()); + return template.getObjectInfo(bucketName, objectName); + + } + + @SneakyThrows + @GetMapping("/object/{bucketName}/{objectName}") + public List filterObject(@PathVariable String bucketName, @PathVariable String objectName) { + + return template.getAllObjectsByPrefix(bucketName, objectName, true); + + } + + @SneakyThrows + @GetMapping("/object/{bucketName}/{objectName}/{expires}") + public Map getObject(@PathVariable String bucketName, @PathVariable String objectName, + @PathVariable Integer expires) { + Map responseBody = new HashMap<>(8); + // Put Object info + responseBody.put("bucket", bucketName); + responseBody.put("object", objectName); + responseBody.put("url", template.getObjectURL(bucketName, objectName, expires)); + responseBody.put("expires", expires); + return responseBody; + } + + @SneakyThrows + @ResponseStatus(HttpStatus.ACCEPTED) + @DeleteMapping("/object/{bucketName}/{objectName}/") + public void deleteObject(@PathVariable String bucketName, @PathVariable String objectName) { + + template.removeObject(bucketName, objectName); + } + +} diff --git a/ruoyi-plugin/ruoyi-springboot-starter-oss/src/main/java/com/ruoyi/plugin/oss/core/OssTemplate.java b/ruoyi-plugin/ruoyi-springboot-starter-oss/src/main/java/com/ruoyi/plugin/oss/core/OssTemplate.java new file mode 100644 index 00000000..74b265af --- /dev/null +++ b/ruoyi-plugin/ruoyi-springboot-starter-oss/src/main/java/com/ruoyi/plugin/oss/core/OssTemplate.java @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2018-2025, lengleng All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * Neither the name of the pig4cloud.com developer nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * Author: lengleng (wangiegie@gmail.com) + */ +package com.ruoyi.plugin.oss.core; + +import com.amazonaws.ClientConfiguration; +import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.auth.AWSCredentialsProvider; +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.client.builder.AwsClientBuilder; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.model.*; +import com.amazonaws.util.IOUtils; +import com.ruoyi.plugin.oss.props.OssProperties; +import lombok.Cleanup; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import org.springframework.beans.factory.InitializingBean; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.net.URL; +import java.util.*; + +/** + * aws-s3 通用存储操作 支持所有兼容s3协议的云存储: {阿里云OSS,腾讯云COS,七牛云,京东云,minio 等} + * + * @author lengleng + * @author 858695266 + * @since 1.0 + */ +@RequiredArgsConstructor +public class OssTemplate implements InitializingBean { + + private final OssProperties ossProperties; + + private AmazonS3 amazonS3; + + /** + * 创建bucket + * + * @param bucketName bucket名称 + */ + @SneakyThrows + public void createBucket(String bucketName) { + if (!amazonS3.doesBucketExistV2(bucketName)) { + amazonS3.createBucket((bucketName)); + } + } + + /** + * 获取全部bucket + *

+ * + * @see AWS + * API Documentation + */ + @SneakyThrows + public List getAllBuckets() { + return amazonS3.listBuckets(); + } + + /** + * @param bucketName bucket名称 + * @see AWS + * API Documentation + */ + @SneakyThrows + public Optional getBucket(String bucketName) { + return amazonS3.listBuckets().stream().filter(b -> b.getName().equals(bucketName)).findFirst(); + } + + /** + * @param bucketName bucket名称 + * @see AWS API + * Documentation + */ + @SneakyThrows + public void removeBucket(String bucketName) { + amazonS3.deleteBucket(bucketName); + } + + /** + * 根据文件前置查询文件 + * + * @param bucketName bucket名称 + * @param prefix 前缀 + * @param recursive 是否递归查询 + * @return S3ObjectSummary 列表 + * @see AWS + * API Documentation + */ + @SneakyThrows + public List getAllObjectsByPrefix(String bucketName, String prefix, boolean recursive) { + ObjectListing objectListing = amazonS3.listObjects(bucketName, prefix); + return new ArrayList<>(objectListing.getObjectSummaries()); + } + + /** + * 获取文件外链 + * + * @param bucketName bucket名称 + * @param objectName 文件名称 + * @param expires 过期时间 <=7 + * @return url + * @see AmazonS3#generatePresignedUrl(String bucketName, String key, Date expiration) + */ + @SneakyThrows + public String getObjectURL(String bucketName, String objectName, Integer expires) { + Date date = new Date(); + Calendar calendar = new GregorianCalendar(); + calendar.setTime(date); + calendar.add(Calendar.DAY_OF_MONTH, expires); + URL url = amazonS3.generatePresignedUrl(bucketName, objectName, calendar.getTime()); + return url.toString(); + } + + /** + * 获取文件URL + *

+ * If the object identified by the given bucket and key has public read permissions + * (ex: {@link CannedAccessControlList#PublicRead}), then this URL can be directly + * accessed to retrieve the object's data. + * + * @param bucketName bucket名称 + * @param objectName 文件名称 + * @return url + */ + public String getObjectURL(String bucketName, String objectName) { + URL url = amazonS3.getUrl(bucketName, objectName); + return url.toString(); + } + + /** + * 获取文件 + * + * @param bucketName bucket名称 + * @param objectName 文件名称 + * @return 二进制流 + * @see AWS + * API Documentation + */ + @SneakyThrows + public InputStream getObject(String bucketName, String objectName) { + return amazonS3.getObject(bucketName, objectName).getObjectContent(); + } + + /** + * 上传文件 + * + * @param bucketName bucket名称 + * @param objectName 文件名称 + * @param stream 文件流 + * @throws Exception + */ + public void putObject(String bucketName, String objectName, InputStream stream) throws Exception { + putObject(bucketName, objectName, stream, (long) stream.available(), "application/octet-stream"); + } + + /** + * 上传文件 + * + * @param bucketName bucket名称 + * @param objectName 文件名称 + * @param stream 文件流 + * @param size 大小 + * @param contextType 类型 + * @throws Exception + * @see AWS + * API Documentation + */ + public PutObjectResult putObject(String bucketName, String objectName, InputStream stream, long size, + String contextType) throws Exception { + // String fileName = getFileName(objectName); + byte[] bytes = IOUtils.toByteArray(stream); + ObjectMetadata objectMetadata = new ObjectMetadata(); + objectMetadata.setContentLength(size); + objectMetadata.setContentType(contextType); + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); + // 上传 + return amazonS3.putObject(bucketName, objectName, byteArrayInputStream, objectMetadata); + + } + + /** + * 获取文件信息 + * + * @param bucketName bucket名称 + * @param objectName 文件名称 + * @throws Exception + * @see AWS + * API Documentation + */ + public S3Object getObjectInfo(String bucketName, String objectName) throws Exception { + @Cleanup S3Object object = amazonS3.getObject(bucketName, objectName); + return object; + } + + /** + * 删除文件 + * + * @param bucketName bucket名称 + * @param objectName 文件名称 + * @throws Exception + * @see AWS API + * Documentation + */ + public void removeObject(String bucketName, String objectName) throws Exception { + amazonS3.deleteObject(bucketName, objectName); + } + + @Override + public void afterPropertiesSet() throws Exception { + ClientConfiguration clientConfiguration = new ClientConfiguration(); + AwsClientBuilder.EndpointConfiguration endpointConfiguration = new AwsClientBuilder.EndpointConfiguration( + ossProperties.getEndpoint(), ossProperties.getRegion()); + AWSCredentials awsCredentials = new BasicAWSCredentials(ossProperties.getAccessKey(), + ossProperties.getSecretKey()); + AWSCredentialsProvider awsCredentialsProvider = new AWSStaticCredentialsProvider(awsCredentials); + this.amazonS3 = AmazonS3Client.builder().withEndpointConfiguration(endpointConfiguration) + .withClientConfiguration(clientConfiguration).withCredentials(awsCredentialsProvider) + .disableChunkedEncoding().withPathStyleAccessEnabled(ossProperties.getPathStyleAccess()).build(); + } + +} diff --git a/ruoyi-plugin/ruoyi-springboot-starter-oss/src/main/java/com/ruoyi/plugin/oss/props/OssProperties.java b/ruoyi-plugin/ruoyi-springboot-starter-oss/src/main/java/com/ruoyi/plugin/oss/props/OssProperties.java new file mode 100644 index 00000000..34775df1 --- /dev/null +++ b/ruoyi-plugin/ruoyi-springboot-starter-oss/src/main/java/com/ruoyi/plugin/oss/props/OssProperties.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2018-2025, lengleng All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * Neither the name of the pig4cloud.com developer nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * Author: lengleng (wangiegie@gmail.com) + */ +package com.ruoyi.plugin.oss.props; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.io.Serializable; + +/** + * AWS配置信息 + * + * @author lengleng + * @author 858695266 + *

+ * oss: enable: true + * endpoint: http://127.0.0.1:9000 # + * pathStyleAccess 采用nginx反向代理或者AWS S3 配置成true,支持第三方云存储配置成false + * pathStyleAccess: false + * access-key: mate + * secret-key: mate + * bucket-name: mate + * region: custom-domain: oss.mate.vip + *

+ */ +@Data +@ConfigurationProperties(prefix = OssProperties.PREFIX) +public class OssProperties implements Serializable { + + private static final long serialVersionUID = 7111600914195056135L; + + + /** + * 配置前缀 + */ + public static final String PREFIX = "oss"; + + /** + * 是否启用 oss,默认为:true + */ + private boolean enable = true; + + /** + * 对象存储服务的URL + */ + private String endpoint; + + /** + * 自定义域名 + */ + private String customDomain; + + /** + * true path-style nginx 反向代理和S3默认支持 pathStyle {http://endpoint/bucketname} false + * supports virtual-hosted-style 阿里云等需要配置为 virtual-hosted-style + * 模式{http://bucketname.endpoint} + */ + private Boolean pathStyleAccess = true; + + /** + * 区域 + */ + private String region; + + /** + * Access key就像用户ID,可以唯一标识你的账户 + */ + private String accessKey; + + /** + * Secret key是你账户的密码 + */ + private String secretKey; + + /** + * 默认的存储桶名称 + */ + private String bucketName; + +} diff --git a/ruoyi-plugin/ruoyi-springboot-starter-oss/src/main/resources/META-INF/spring.factories b/ruoyi-plugin/ruoyi-springboot-starter-oss/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..6d49fd9c --- /dev/null +++ b/ruoyi-plugin/ruoyi-springboot-starter-oss/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + com.ruoyi.plugin.oss.config.OssConfiguration diff --git a/ruoyi-plugin/ruoyi-springboot-starter-oss/target/classes/META-INF/spring.factories b/ruoyi-plugin/ruoyi-springboot-starter-oss/target/classes/META-INF/spring.factories new file mode 100644 index 00000000..6d49fd9c --- /dev/null +++ b/ruoyi-plugin/ruoyi-springboot-starter-oss/target/classes/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + com.ruoyi.plugin.oss.config.OssConfiguration diff --git a/ruoyi-plugin/ruoyi-springboot-starter-oss/target/classes/com/ruoyi/plugin/oss/config/OssConfiguration.class b/ruoyi-plugin/ruoyi-springboot-starter-oss/target/classes/com/ruoyi/plugin/oss/config/OssConfiguration.class new file mode 100644 index 00000000..9a927c6c Binary files /dev/null and b/ruoyi-plugin/ruoyi-springboot-starter-oss/target/classes/com/ruoyi/plugin/oss/config/OssConfiguration.class differ diff --git a/ruoyi-plugin/ruoyi-springboot-starter-oss/target/classes/com/ruoyi/plugin/oss/controller/OssEndpoint.class b/ruoyi-plugin/ruoyi-springboot-starter-oss/target/classes/com/ruoyi/plugin/oss/controller/OssEndpoint.class new file mode 100644 index 00000000..2987f43e Binary files /dev/null and b/ruoyi-plugin/ruoyi-springboot-starter-oss/target/classes/com/ruoyi/plugin/oss/controller/OssEndpoint.class differ diff --git a/ruoyi-plugin/ruoyi-springboot-starter-oss/target/classes/com/ruoyi/plugin/oss/core/OssTemplate.class b/ruoyi-plugin/ruoyi-springboot-starter-oss/target/classes/com/ruoyi/plugin/oss/core/OssTemplate.class new file mode 100644 index 00000000..cde35eaa Binary files /dev/null and b/ruoyi-plugin/ruoyi-springboot-starter-oss/target/classes/com/ruoyi/plugin/oss/core/OssTemplate.class differ diff --git a/ruoyi-plugin/ruoyi-springboot-starter-oss/target/classes/com/ruoyi/plugin/oss/props/OssProperties.class b/ruoyi-plugin/ruoyi-springboot-starter-oss/target/classes/com/ruoyi/plugin/oss/props/OssProperties.class new file mode 100644 index 00000000..c6e6b05a Binary files /dev/null and b/ruoyi-plugin/ruoyi-springboot-starter-oss/target/classes/com/ruoyi/plugin/oss/props/OssProperties.class differ diff --git a/ruoyi-plugin/ruoyi-springboot-starter-tenant/pom.xml b/ruoyi-plugin/ruoyi-springboot-starter-tenant/pom.xml new file mode 100644 index 00000000..688717a3 --- /dev/null +++ b/ruoyi-plugin/ruoyi-springboot-starter-tenant/pom.xml @@ -0,0 +1,39 @@ + + + + ruoyi-plugin + com.ruoyi + 3.8.4 + + 4.0.0 + + ruoyi-springboot-starter-tenant + + + 多租户插件 + + + + + + com.ruoyi + ruoyi-common + + + + + org.projectlombok + lombok + true + + + + + org.springframework.boot + spring-boot-starter-aop + + + + diff --git a/ruoyi-plugin/ruoyi-springboot-starter-tenant/src/main/java/com/ruoyi/plugin/tenant/config/TenantConfiguration.java b/ruoyi-plugin/ruoyi-springboot-starter-tenant/src/main/java/com/ruoyi/plugin/tenant/config/TenantConfiguration.java new file mode 100644 index 00000000..50dccd81 --- /dev/null +++ b/ruoyi-plugin/ruoyi-springboot-starter-tenant/src/main/java/com/ruoyi/plugin/tenant/config/TenantConfiguration.java @@ -0,0 +1,63 @@ +package com.ruoyi.plugin.tenant.config; + +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; +import com.ruoyi.common.mybatis.util.MyBatisUtils; +import com.ruoyi.plugin.tenant.core.aop.TenantIgnoreAspect; +import com.ruoyi.plugin.tenant.core.db.TenantDatabaseInterceptor; +import com.ruoyi.plugin.tenant.core.security.TenantSecurityWebFilter; +import com.ruoyi.plugin.tenant.core.web.TenantContextWebFilter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ConditionalOnProperty(prefix = "ruoyi.tenant", value = "enable", matchIfMissing = true) // 允许使用 yudao.tenant.enable=false 禁用多租户 +@EnableConfigurationProperties(TenantProperties.class) +public class TenantConfiguration { +// +// @Bean +// public TenantFrameworkService tenantFrameworkService(TenantApi tenantApi) { +// return new TenantFrameworkServiceImpl(tenantApi); +// } + + // ========== AOP ========== + + @Bean + public TenantIgnoreAspect tenantIgnoreAspect() { + return new TenantIgnoreAspect(); + } + + // ========== DB ========== + + @Bean + public TenantLineInnerInterceptor tenantLineInnerInterceptor(TenantProperties properties, MybatisPlusInterceptor interceptor) { + TenantLineInnerInterceptor inner = new TenantLineInnerInterceptor(new TenantDatabaseInterceptor(properties)); + // 添加到 interceptor 中 + // 需要加在首个,主要是为了在分页插件前面。这个是 MyBatis Plus 的规定 + MyBatisUtils.addInterceptor(interceptor, inner, 0); + return inner; + } + +// ========== WEB ========== + + @Bean + public FilterRegistrationBean tenantContextWebFilter() { + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + registrationBean.setFilter(new TenantContextWebFilter()); + registrationBean.setOrder(-104); + return registrationBean; + } + + // ========== Security ========== + + @Bean + public FilterRegistrationBean tenantSecurityWebFilter(TenantProperties tenantProperties) { + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + registrationBean.setFilter(new TenantSecurityWebFilter(tenantProperties)); + registrationBean.setOrder(-99); + return registrationBean; + } +} diff --git a/ruoyi-plugin/ruoyi-springboot-starter-tenant/src/main/java/com/ruoyi/plugin/tenant/config/TenantProperties.java b/ruoyi-plugin/ruoyi-springboot-starter-tenant/src/main/java/com/ruoyi/plugin/tenant/config/TenantProperties.java new file mode 100644 index 00000000..f01af7fd --- /dev/null +++ b/ruoyi-plugin/ruoyi-springboot-starter-tenant/src/main/java/com/ruoyi/plugin/tenant/config/TenantProperties.java @@ -0,0 +1,40 @@ +package com.ruoyi.plugin.tenant.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.util.Collections; +import java.util.Set; + +/** + * 多租户配置 + * + * @author 芋道源码 + */ +@ConfigurationProperties(prefix = "ruoyi.tenant") +@Data +public class TenantProperties { + /** + * 租户是否开启 + */ + private static final Boolean ENABLE_DEFAULT = true; + + /** + * 是否开启 + */ + private Boolean enable = ENABLE_DEFAULT; + /** + * 需要忽略多租户的请求 + * + * 默认情况下,每个请求需要带上 tenant-id 的请求头。但是,部分请求是无需带上的,例如说短信回调、支付回调等 Open API! + */ + private Set ignoreUrls = Collections.emptySet(); + +// /** +// * 多租户的表 +// * +// * 即默认所有表都开启多租户的功能,所以记得添加对应的 tenant_id 字段哟 +// */ +// private Set noignoreTables = Collections.emptySet(); + +} diff --git a/ruoyi-plugin/ruoyi-springboot-starter-tenant/src/main/java/com/ruoyi/plugin/tenant/core/aop/TenantIgnore.java b/ruoyi-plugin/ruoyi-springboot-starter-tenant/src/main/java/com/ruoyi/plugin/tenant/core/aop/TenantIgnore.java new file mode 100644 index 00000000..f6370c5b --- /dev/null +++ b/ruoyi-plugin/ruoyi-springboot-starter-tenant/src/main/java/com/ruoyi/plugin/tenant/core/aop/TenantIgnore.java @@ -0,0 +1,18 @@ +package com.ruoyi.plugin.tenant.core.aop; + +import java.lang.annotation.*; + +/** + * 忽略租户,标记指定方法不进行租户的自动过滤 + * + * 注意,只有 DB 的场景会过滤,其它场景暂时不过滤: + * 1、Redis 场景:因为是基于 Key 实现多租户的能力,所以忽略没有意义,不像 DB 是一个 column 实现的 + * 2、MQ 场景:有点难以抉择,目前可以通过 Consumer 手动在消费的方法上,添加 @TenantIgnore 进行忽略 + * + * @author 芋道源码 + */ +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface TenantIgnore { +} diff --git a/ruoyi-plugin/ruoyi-springboot-starter-tenant/src/main/java/com/ruoyi/plugin/tenant/core/aop/TenantIgnoreAspect.java b/ruoyi-plugin/ruoyi-springboot-starter-tenant/src/main/java/com/ruoyi/plugin/tenant/core/aop/TenantIgnoreAspect.java new file mode 100644 index 00000000..b58d7f93 --- /dev/null +++ b/ruoyi-plugin/ruoyi-springboot-starter-tenant/src/main/java/com/ruoyi/plugin/tenant/core/aop/TenantIgnoreAspect.java @@ -0,0 +1,32 @@ +package com.ruoyi.plugin.tenant.core.aop; + +import com.ruoyi.plugin.tenant.core.context.TenantContextHolder; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; + +/** + * 忽略多租户的 Aspect,基于 {@link TenantIgnore} 注解实现,用于一些全局的逻辑。 + * 例如说,一个定时任务,读取所有数据,进行处理。 + * 又例如说,读取所有数据,进行缓存。 + * + * @author 芋道源码 + */ +@Aspect +@Slf4j +public class TenantIgnoreAspect { + + @Around("@annotation(tenantIgnore)") + public Object around(ProceedingJoinPoint joinPoint, TenantIgnore tenantIgnore) throws Throwable { + Boolean oldIgnore = TenantContextHolder.isIgnore(); + try { + TenantContextHolder.setIgnore(true); + // 执行逻辑 + return joinPoint.proceed(); + } finally { + TenantContextHolder.setIgnore(oldIgnore); + } + } + +} diff --git a/ruoyi-plugin/ruoyi-springboot-starter-tenant/src/main/java/com/ruoyi/plugin/tenant/core/context/TenantContextHolder.java b/ruoyi-plugin/ruoyi-springboot-starter-tenant/src/main/java/com/ruoyi/plugin/tenant/core/context/TenantContextHolder.java new file mode 100644 index 00000000..37e2997a --- /dev/null +++ b/ruoyi-plugin/ruoyi-springboot-starter-tenant/src/main/java/com/ruoyi/plugin/tenant/core/context/TenantContextHolder.java @@ -0,0 +1,66 @@ +package com.ruoyi.plugin.tenant.core.context; + +import com.alibaba.ttl.TransmittableThreadLocal; + +/** + * 多租户上下文 Holder + * + * @author 芋道源码 + */ +public class TenantContextHolder { + + /** + * 当前租户编号 + */ + private static final ThreadLocal TENANT_ID = new TransmittableThreadLocal<>(); + + /** + * 是否忽略租户 + */ + private static final ThreadLocal IGNORE = new TransmittableThreadLocal<>(); + + /** + * 获得租户编号。 + * + * @return 租户编号 + */ + public static Long getTenantId() { + return TENANT_ID.get(); + } + + /** + * 获得租户编号。如果不存在,则抛出 NullPointerException 异常 + * + * @return 租户编号 + */ + public static Long getRequiredTenantId() { + Long tenantId = getTenantId(); + if (tenantId == null) { + throw new NullPointerException("TenantContextHolder 不存在租户编号"); // TODO 芋艿:增加文档链接 + } + return tenantId; + } + + public static void setTenantId(Long tenantId) { + TENANT_ID.set(tenantId); + } + + public static void setIgnore(Boolean ignore) { + IGNORE.set(ignore); + } + + /** + * 当前是否忽略租户 + * + * @return 是否忽略 + */ + public static boolean isIgnore() { + return Boolean.TRUE.equals(IGNORE.get()); + } + + public static void clear() { + TENANT_ID.remove(); + IGNORE.remove(); + } + +} diff --git a/ruoyi-plugin/ruoyi-springboot-starter-tenant/src/main/java/com/ruoyi/plugin/tenant/core/db/TenantBaseDO.java b/ruoyi-plugin/ruoyi-springboot-starter-tenant/src/main/java/com/ruoyi/plugin/tenant/core/db/TenantBaseDO.java new file mode 100644 index 00000000..59fb0023 --- /dev/null +++ b/ruoyi-plugin/ruoyi-springboot-starter-tenant/src/main/java/com/ruoyi/plugin/tenant/core/db/TenantBaseDO.java @@ -0,0 +1,21 @@ +package com.ruoyi.plugin.tenant.core.db; + +import com.ruoyi.common.mybatis.dataobject.BaseDO; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 拓展多租户的 BaseDO 基类 + * + * @author 芋道源码 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public abstract class TenantBaseDO extends BaseDO { + + /** + * 多租户编号 + */ + private Long tenantId; + +} diff --git a/ruoyi-plugin/ruoyi-springboot-starter-tenant/src/main/java/com/ruoyi/plugin/tenant/core/db/TenantDatabaseInterceptor.java b/ruoyi-plugin/ruoyi-springboot-starter-tenant/src/main/java/com/ruoyi/plugin/tenant/core/db/TenantDatabaseInterceptor.java new file mode 100644 index 00000000..d9de74c3 --- /dev/null +++ b/ruoyi-plugin/ruoyi-springboot-starter-tenant/src/main/java/com/ruoyi/plugin/tenant/core/db/TenantDatabaseInterceptor.java @@ -0,0 +1,66 @@ +package com.ruoyi.plugin.tenant.core.db; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler; +import com.ruoyi.plugin.tenant.config.TenantProperties; +import com.ruoyi.plugin.tenant.core.context.TenantContextHolder; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.LongValue; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * 基于 MyBatis Plus 多租户的功能,实现 DB 层面的多租户的功能 + * + * @author 芋道源码 + */ +public class TenantDatabaseInterceptor implements TenantLineHandler { + + private final Set noIgnoreTables = new HashSet<>(); + /** + *多租户的系统类相关表 + */ + public static List tables = + Arrays.asList( "sys_role_menu", "sys_logininfor", "sys_user", "sys_post","sys_user_post", "sys_role_dept", "sys_role_menu", "sys_role", "sys_dept", "sys_user_role", "sys_oper_log","sys_notice"); + + /** + * 业务/工作流非多租户的相关表 + */ + public static List bstables = + Arrays.asList("bs_product"); + public TenantDatabaseInterceptor(TenantProperties properties) { + // 不同 DB 下,大小写的习惯不同,所以需要都添加进去 + tables.forEach(table -> { + noIgnoreTables.add(table.toLowerCase()); + noIgnoreTables.add(table.toUpperCase()); + }); + // 在 OracleKeyGenerator 中,生成主键时,会查询这个表,查询这个表后,会自动拼接 TENANT_ID 导致报错 + noIgnoreTables.add("DUAL"); + } + + @Override + public Expression getTenantId() { + return new LongValue(TenantContextHolder.getRequiredTenantId()); + } + + @Override + public boolean ignoreTable(String tableName) { + if (TenantContextHolder.isIgnore()) { + // 情况一,全局忽略多租户 + return true; + } + if (tableName.indexOf("bs_") ==0 || tableName.indexOf("bpm_") ==0) { + //业务相关表 + if (bstables.contains(tableName)) { + return true; + }else { + return false; + } + } + return !CollUtil.contains(noIgnoreTables, tableName); // 情况二,忽略非多租户的系统基础功能表 + } + +} diff --git a/ruoyi-plugin/ruoyi-springboot-starter-tenant/src/main/java/com/ruoyi/plugin/tenant/core/security/TenantSecurityWebFilter.java b/ruoyi-plugin/ruoyi-springboot-starter-tenant/src/main/java/com/ruoyi/plugin/tenant/core/security/TenantSecurityWebFilter.java new file mode 100644 index 00000000..2f56d2ae --- /dev/null +++ b/ruoyi-plugin/ruoyi-springboot-starter-tenant/src/main/java/com/ruoyi/plugin/tenant/core/security/TenantSecurityWebFilter.java @@ -0,0 +1,102 @@ +package com.ruoyi.plugin.tenant.core.security; + +import cn.hutool.core.collection.CollUtil; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.filter.ApiRequestFilter; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.plugin.tenant.config.TenantProperties; +import com.ruoyi.plugin.tenant.core.context.TenantContextHolder; +import lombok.extern.slf4j.Slf4j; +import org.springframework.util.AntPathMatcher; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Objects; + +/** + * 多租户 Security Web 过滤器 + * 1. 如果是登陆的用户,校验是否有权限访问该租户,避免越权问题。 + * 2. 如果请求未带租户的编号,检查是否是忽略的 URL,否则也不允许访问。 + * 3. 校验租户是合法,例如说被禁用、到期 + *

+ * 校验用户访问的租户,是否是其所在的租户, + * + * @author 芋道源码 + */ +@Slf4j +public class TenantSecurityWebFilter extends ApiRequestFilter { + + private final TenantProperties tenantProperties; + + private final AntPathMatcher pathMatcher; + + + public TenantSecurityWebFilter(TenantProperties tenantProperties) { + this.tenantProperties = tenantProperties; + this.pathMatcher = new AntPathMatcher(); + } + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { + Long tenantId = TenantContextHolder.getTenantId(); + // 1. 登陆的用户,校验是否有权限访问该租户,避免越权问题。 + LoginUser user = SecurityUtils.getLoginUserNoExcePtion(); + if (user != null) { + // 如果获取不到租户编号,则尝试使用登陆用户的租户编号 + if (tenantId == null) { + tenantId = user.getTenantId(); + TenantContextHolder.setTenantId(tenantId); + // 如果传递了租户编号,则进行比对租户编号,避免越权问题 + } else if (!Objects.equals(user.getTenantId(), TenantContextHolder.getTenantId())) { + log.error("[doFilterInternal][租户({}) User({}/{}) 越权访问租户({}) URL({}/{})]", user.getTenantId(), user.getUserId(), "admin", TenantContextHolder.getTenantId(), request.getRequestURI(), request.getMethod()); + ServletUtils.writeJSON(response, R.fail("您无权访问该租户的数据")); + return; + } + } + + // 如果非允许忽略租户的 URL,则校验租户是否合法 + if (!isIgnoreUrl(request)) { + // 2. 如果请求未带租户的编号,不允许访问。 + if (tenantId == null) { + log.error("[doFilterInternal][URL({}/{}) 未传递租户编号]", request.getRequestURI(), request.getMethod()); + ServletUtils.writeJSON(response, R.fail("租户的请求未传递,请进行排查")); + return; + } + // 3. 校验租户是合法,例如说被禁用、到期 cqptodo +// try { +// tenantFrameworkService.validTenant(tenantId); +// } catch (Throwable ex) { +// R result = globalExceptionHandler.allExceptionHandler(request, ex); +// ServletUtils.writeJSON(response, result); +// return; +// } + } else { // 如果是允许忽略租户的 URL,若未传递租户编号,则默认忽略租户编号,避免报错 + if (tenantId == null) { + TenantContextHolder.setIgnore(true); + } + } + + // 继续过滤 + chain.doFilter(request, response); + } + + private boolean isIgnoreUrl(HttpServletRequest request) { + // 快速匹配,保证性能 + if (CollUtil.contains(tenantProperties.getIgnoreUrls(), request.getRequestURI())) { + return true; + } + // 逐个 Ant 路径匹配 + for (String url : tenantProperties.getIgnoreUrls()) { + if (pathMatcher.match(url, request.getRequestURI())) { + return true; + } + } + return false; + } + +} diff --git a/ruoyi-plugin/ruoyi-springboot-starter-tenant/src/main/java/com/ruoyi/plugin/tenant/core/util/TenantUtils.java b/ruoyi-plugin/ruoyi-springboot-starter-tenant/src/main/java/com/ruoyi/plugin/tenant/core/util/TenantUtils.java new file mode 100644 index 00000000..2febb100 --- /dev/null +++ b/ruoyi-plugin/ruoyi-springboot-starter-tenant/src/main/java/com/ruoyi/plugin/tenant/core/util/TenantUtils.java @@ -0,0 +1,49 @@ +package com.ruoyi.plugin.tenant.core.util; + + +import cn.hutool.core.util.StrUtil; +import com.ruoyi.plugin.tenant.core.context.TenantContextHolder; + +import javax.servlet.http.HttpServletRequest; + +/** + * 多租户 Util + * + * @author 芋道源码 + */ +public class TenantUtils { + public static final String HEADER_TENANT_ID = "tenant-id"; + /** + * 使用指定租户,执行对应的逻辑 + * + * 注意,如果当前是忽略租户的情况下,会被强制设置成不忽略租户 + * 当然,执行完成后,还是会恢复回去 + * + * @param tenantId 租户编号 + * @param runnable 逻辑 + */ + public static void execute(Long tenantId, Runnable runnable) { + Long oldTenantId = TenantContextHolder.getTenantId(); + Boolean oldIgnore = TenantContextHolder.isIgnore(); + try { + TenantContextHolder.setTenantId(tenantId); + TenantContextHolder.setIgnore(false); + // 执行逻辑 + runnable.run(); + } finally { + TenantContextHolder.setTenantId(oldTenantId); + TenantContextHolder.setIgnore(oldIgnore); + } + } + /** + * 获得租户编号,从 header 中 + * 考虑到其它 framework 组件也会使用到租户编号,所以不得不放在 WebFrameworkUtils 统一提供 + * + * @param request 请求 + * @return 租户编号 + */ + public static Long getTenantId(HttpServletRequest request) { + String tenantId = request.getHeader(HEADER_TENANT_ID); + return StrUtil.isNotEmpty(tenantId) ? Long.valueOf(tenantId) : null; + } +} diff --git a/ruoyi-plugin/ruoyi-springboot-starter-tenant/src/main/java/com/ruoyi/plugin/tenant/core/web/TenantContextWebFilter.java b/ruoyi-plugin/ruoyi-springboot-starter-tenant/src/main/java/com/ruoyi/plugin/tenant/core/web/TenantContextWebFilter.java new file mode 100644 index 00000000..f9e2648b --- /dev/null +++ b/ruoyi-plugin/ruoyi-springboot-starter-tenant/src/main/java/com/ruoyi/plugin/tenant/core/web/TenantContextWebFilter.java @@ -0,0 +1,44 @@ +package com.ruoyi.plugin.tenant.core.web; + +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.plugin.tenant.core.context.TenantContextHolder; +import com.ruoyi.plugin.tenant.core.util.TenantUtils; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * 多租户 Context Web 过滤器 + * 将请求 Header 中的 tenant-id 解析出来,添加到 {@link TenantContextHolder} 中,这样后续的 DB 等操作,可以获得到租户编号。 + * + * @author 芋道源码 + */ +public class TenantContextWebFilter extends OncePerRequestFilter { + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { + // 设置 + Long tenantId = TenantUtils.getTenantId(request); + SecurityContext securityContext = SecurityContextHolder.getContext(); + if (securityContext != null && securityContext.getAuthentication() != null && securityContext.getAuthentication().isAuthenticated()) { + //已登录直接取用户的租户ID。这样判断是因为未登录的接口就需要从请求头获取了 + tenantId = SecurityUtils.getLoginUser().getTenantId(); + } + if (tenantId != null) { + TenantContextHolder.setTenantId(tenantId); + } + try { + chain.doFilter(request, response); + } finally { + // 清理 + TenantContextHolder.clear(); + } + } + +} diff --git a/ruoyi-plugin/ruoyi-springboot-starter-tenant/src/main/resources/META-INF/spring.factories b/ruoyi-plugin/ruoyi-springboot-starter-tenant/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..5b27b46c --- /dev/null +++ b/ruoyi-plugin/ruoyi-springboot-starter-tenant/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + com.ruoyi.plugin.tenant.config.TenantConfiguration diff --git a/ruoyi-plugin/ruoyi-springboot-starter-tenant/target/classes/META-INF/spring.factories b/ruoyi-plugin/ruoyi-springboot-starter-tenant/target/classes/META-INF/spring.factories new file mode 100644 index 00000000..5b27b46c --- /dev/null +++ b/ruoyi-plugin/ruoyi-springboot-starter-tenant/target/classes/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + com.ruoyi.plugin.tenant.config.TenantConfiguration diff --git a/ruoyi-plugin/ruoyi-springboot-starter-tenant/target/classes/com/ruoyi/plugin/tenant/config/TenantConfiguration.class b/ruoyi-plugin/ruoyi-springboot-starter-tenant/target/classes/com/ruoyi/plugin/tenant/config/TenantConfiguration.class new file mode 100644 index 00000000..47145a6a Binary files /dev/null and b/ruoyi-plugin/ruoyi-springboot-starter-tenant/target/classes/com/ruoyi/plugin/tenant/config/TenantConfiguration.class differ diff --git a/ruoyi-plugin/ruoyi-springboot-starter-tenant/target/classes/com/ruoyi/plugin/tenant/config/TenantProperties.class b/ruoyi-plugin/ruoyi-springboot-starter-tenant/target/classes/com/ruoyi/plugin/tenant/config/TenantProperties.class new file mode 100644 index 00000000..a78800d4 Binary files /dev/null and b/ruoyi-plugin/ruoyi-springboot-starter-tenant/target/classes/com/ruoyi/plugin/tenant/config/TenantProperties.class differ diff --git a/ruoyi-plugin/ruoyi-springboot-starter-tenant/target/classes/com/ruoyi/plugin/tenant/core/aop/TenantIgnore.class b/ruoyi-plugin/ruoyi-springboot-starter-tenant/target/classes/com/ruoyi/plugin/tenant/core/aop/TenantIgnore.class new file mode 100644 index 00000000..b84f9461 Binary files /dev/null and b/ruoyi-plugin/ruoyi-springboot-starter-tenant/target/classes/com/ruoyi/plugin/tenant/core/aop/TenantIgnore.class differ diff --git a/ruoyi-plugin/ruoyi-springboot-starter-tenant/target/classes/com/ruoyi/plugin/tenant/core/aop/TenantIgnoreAspect.class b/ruoyi-plugin/ruoyi-springboot-starter-tenant/target/classes/com/ruoyi/plugin/tenant/core/aop/TenantIgnoreAspect.class new file mode 100644 index 00000000..7d80da96 Binary files /dev/null and b/ruoyi-plugin/ruoyi-springboot-starter-tenant/target/classes/com/ruoyi/plugin/tenant/core/aop/TenantIgnoreAspect.class differ diff --git a/ruoyi-plugin/ruoyi-springboot-starter-tenant/target/classes/com/ruoyi/plugin/tenant/core/context/TenantContextHolder.class b/ruoyi-plugin/ruoyi-springboot-starter-tenant/target/classes/com/ruoyi/plugin/tenant/core/context/TenantContextHolder.class new file mode 100644 index 00000000..4c860985 Binary files /dev/null and b/ruoyi-plugin/ruoyi-springboot-starter-tenant/target/classes/com/ruoyi/plugin/tenant/core/context/TenantContextHolder.class differ diff --git a/ruoyi-plugin/ruoyi-springboot-starter-tenant/target/classes/com/ruoyi/plugin/tenant/core/db/TenantBaseDO.class b/ruoyi-plugin/ruoyi-springboot-starter-tenant/target/classes/com/ruoyi/plugin/tenant/core/db/TenantBaseDO.class new file mode 100644 index 00000000..29cef21e Binary files /dev/null and b/ruoyi-plugin/ruoyi-springboot-starter-tenant/target/classes/com/ruoyi/plugin/tenant/core/db/TenantBaseDO.class differ diff --git a/ruoyi-plugin/ruoyi-springboot-starter-tenant/target/classes/com/ruoyi/plugin/tenant/core/db/TenantDatabaseInterceptor.class b/ruoyi-plugin/ruoyi-springboot-starter-tenant/target/classes/com/ruoyi/plugin/tenant/core/db/TenantDatabaseInterceptor.class new file mode 100644 index 00000000..7d3c4461 Binary files /dev/null and b/ruoyi-plugin/ruoyi-springboot-starter-tenant/target/classes/com/ruoyi/plugin/tenant/core/db/TenantDatabaseInterceptor.class differ diff --git a/ruoyi-plugin/ruoyi-springboot-starter-tenant/target/classes/com/ruoyi/plugin/tenant/core/security/TenantSecurityWebFilter.class b/ruoyi-plugin/ruoyi-springboot-starter-tenant/target/classes/com/ruoyi/plugin/tenant/core/security/TenantSecurityWebFilter.class new file mode 100644 index 00000000..ad3ec1e8 Binary files /dev/null and b/ruoyi-plugin/ruoyi-springboot-starter-tenant/target/classes/com/ruoyi/plugin/tenant/core/security/TenantSecurityWebFilter.class differ diff --git a/ruoyi-plugin/ruoyi-springboot-starter-tenant/target/classes/com/ruoyi/plugin/tenant/core/util/TenantUtils.class b/ruoyi-plugin/ruoyi-springboot-starter-tenant/target/classes/com/ruoyi/plugin/tenant/core/util/TenantUtils.class new file mode 100644 index 00000000..5b74ba79 Binary files /dev/null and b/ruoyi-plugin/ruoyi-springboot-starter-tenant/target/classes/com/ruoyi/plugin/tenant/core/util/TenantUtils.class differ diff --git a/ruoyi-plugin/ruoyi-springboot-starter-tenant/target/classes/com/ruoyi/plugin/tenant/core/web/TenantContextWebFilter.class b/ruoyi-plugin/ruoyi-springboot-starter-tenant/target/classes/com/ruoyi/plugin/tenant/core/web/TenantContextWebFilter.class new file mode 100644 index 00000000..579e373b Binary files /dev/null and b/ruoyi-plugin/ruoyi-springboot-starter-tenant/target/classes/com/ruoyi/plugin/tenant/core/web/TenantContextWebFilter.class differ diff --git a/ruoyi-quartz/pom.xml b/ruoyi-quartz/pom.xml new file mode 100644 index 00000000..863fcabd --- /dev/null +++ b/ruoyi-quartz/pom.xml @@ -0,0 +1,40 @@ + + + + ruoyi + com.ruoyi + 3.8.4 + + 4.0.0 + + ruoyi-quartz + + + quartz定时任务 + + + + + + + org.quartz-scheduler + quartz + + + com.mchange + c3p0 + + + + + + + com.ruoyi + ruoyi-common + + + + + diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/config/ScheduleConfig.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/config/ScheduleConfig.java new file mode 100644 index 00000000..a558170c --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/config/ScheduleConfig.java @@ -0,0 +1,57 @@ +//package com.ruoyi.quartz.config; +// +//import org.springframework.context.annotation.Bean; +//import org.springframework.context.annotation.Configuration; +//import org.springframework.scheduling.quartz.SchedulerFactoryBean; +//import javax.sql.DataSource; +//import java.util.Properties; +// +///** +// * 定时任务配置(单机部署建议删除此类和qrtz数据库表,默认走内存会最高效) +// * +// * @author ruoyi +// */ +//@Configuration +//public class ScheduleConfig +//{ +// @Bean +// public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) +// { +// SchedulerFactoryBean factory = new SchedulerFactoryBean(); +// factory.setDataSource(dataSource); +// +// // quartz参数 +// Properties prop = new Properties(); +// prop.put("org.quartz.scheduler.instanceName", "RuoyiScheduler"); +// prop.put("org.quartz.scheduler.instanceId", "AUTO"); +// // 线程池配置 +// prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool"); +// prop.put("org.quartz.threadPool.threadCount", "20"); +// prop.put("org.quartz.threadPool.threadPriority", "5"); +// // JobStore配置 +// prop.put("org.quartz.jobStore.class", "org.springframework.scheduling.quartz.LocalDataSourceJobStore"); +// // 集群配置 +// prop.put("org.quartz.jobStore.isClustered", "true"); +// prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000"); +// prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1"); +// prop.put("org.quartz.jobStore.txIsolationLevelSerializable", "true"); +// +// // sqlserver 启用 +// // prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?"); +// prop.put("org.quartz.jobStore.misfireThreshold", "12000"); +// prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_"); +// factory.setQuartzProperties(prop); +// +// factory.setSchedulerName("RuoyiScheduler"); +// // 延时启动 +// factory.setStartupDelay(1); +// factory.setApplicationContextSchedulerContextKey("applicationContextKey"); +// // 可选,QuartzScheduler +// // 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了 +// factory.setOverwriteExistingJobs(true); +// // 设置自动启动,默认为true +// factory.setAutoStartup(true); +// +// return factory; +// } +//} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobController.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobController.java new file mode 100644 index 00000000..a3f3e1c6 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobController.java @@ -0,0 +1,148 @@ +package com.ruoyi.quartz.controller; + +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.exception.job.TaskException; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.quartz.domain.SysJob; +import com.ruoyi.quartz.service.ISysJobService; +import com.ruoyi.quartz.util.CronUtils; +import com.ruoyi.quartz.util.ScheduleUtils; +import org.quartz.SchedulerException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 调度任务信息操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/job") +public class SysJobController extends BaseController { + @Autowired + private ISysJobService jobService; + + /** + * 查询定时任务列表 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:list')") + @GetMapping("/list") + public TableDataInfo list(SysJob sysJob) { + startPage(); + List list = jobService.selectJobList(sysJob); + return getDataTable(list); + } + + /** + * 导出定时任务列表 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:export')") + @Log(title = "定时任务", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, SysJob sysJob) { + List list = jobService.selectJobList(sysJob); + ExcelUtil util = new ExcelUtil(SysJob.class); + util.exportExcel(response, list, "定时任务"); + } + + /** + * 获取定时任务详细信息 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:query')") + @GetMapping(value = "/{jobId}") + public AjaxResult getInfo(@PathVariable("jobId") Long jobId) { + return success(jobService.selectJobById(jobId)); + } + + /** + * 新增定时任务 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:add')") + @Log(title = "定时任务", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody SysJob job) throws SchedulerException, TaskException { + if (!CronUtils.isValid(job.getCronExpression())) { + return error("新增任务'" + job.getJobName() + "'失败,Cron表达式不正确"); + } else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI)) { + return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi'调用"); + } else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[]{Constants.LOOKUP_LDAP, Constants.LOOKUP_LDAPS})) { + return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap(s)'调用"); + } else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[]{Constants.HTTP, Constants.HTTPS})) { + return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)'调用"); + } else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR)) { + return error("新增任务'" + job.getJobName() + "'失败,目标字符串存在违规"); + } else if (!ScheduleUtils.whiteList(job.getInvokeTarget())) { + return error("新增任务'" + job.getJobName() + "'失败,目标字符串不在白名单内"); + } + job.setCreateBy(getUsername()); + return toAjax(jobService.insertJob(job)); + } + + /** + * 修改定时任务 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:edit')") + @Log(title = "定时任务", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody SysJob job) throws SchedulerException, TaskException { + if (!CronUtils.isValid(job.getCronExpression())) { + return error("修改任务'" + job.getJobName() + "'失败,Cron表达式不正确"); + } else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI)) { + return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi'调用"); + } else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[]{Constants.LOOKUP_LDAP, Constants.LOOKUP_LDAPS})) { + return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap(s)'调用"); + } else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[]{Constants.HTTP, Constants.HTTPS})) { + return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)'调用"); + } else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR)) { + return error("修改任务'" + job.getJobName() + "'失败,目标字符串存在违规"); + } else if (!ScheduleUtils.whiteList(job.getInvokeTarget())) { + return error("修改任务'" + job.getJobName() + "'失败,目标字符串不在白名单内"); + } + job.setUpdateBy(getUsername()); + return toAjax(jobService.updateJob(job)); + } + + /** + * 定时任务状态修改 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:changeStatus')") + @Log(title = "定时任务", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public AjaxResult changeStatus(@RequestBody SysJob job) throws SchedulerException { + SysJob newJob = jobService.selectJobById(job.getJobId()); + newJob.setStatus(job.getStatus()); + return toAjax(jobService.changeStatus(newJob)); + } + + /** + * 定时任务立即执行一次 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:changeStatus')") + @Log(title = "定时任务", businessType = BusinessType.UPDATE) + @PutMapping("/run") + public AjaxResult run(@RequestBody SysJob job) throws SchedulerException { + boolean result = jobService.run(job); + return result ? success() : error("任务不存在或已过期!"); + } + + /** + * 删除定时任务 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:remove')") + @Log(title = "定时任务", businessType = BusinessType.DELETE) + @DeleteMapping("/{jobIds}") + public AjaxResult remove(@PathVariable Long[] jobIds) throws SchedulerException, TaskException { + jobService.deleteJobByIds(jobIds); + return success(); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobLogController.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobLogController.java new file mode 100644 index 00000000..24acf418 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobLogController.java @@ -0,0 +1,82 @@ +package com.ruoyi.quartz.controller; + +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.quartz.domain.SysJobLog; +import com.ruoyi.quartz.service.ISysJobLogService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 调度日志操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/jobLog") +public class SysJobLogController extends BaseController { + @Autowired + private ISysJobLogService jobLogService; + + /** + * 查询定时任务调度日志列表 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:list')") + @GetMapping("/list") + public TableDataInfo list(SysJobLog sysJobLog) { + startPage(); + List list = jobLogService.selectJobLogList(sysJobLog); + return getDataTable(list); + } + + /** + * 导出定时任务调度日志列表 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:export')") + @Log(title = "任务调度日志", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, SysJobLog sysJobLog) { + List list = jobLogService.selectJobLogList(sysJobLog); + ExcelUtil util = new ExcelUtil(SysJobLog.class); + util.exportExcel(response, list, "调度日志"); + } + + /** + * 根据调度编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:query')") + @GetMapping(value = "/{configId}") + public AjaxResult getInfo(@PathVariable Long jobLogId) { + return success(jobLogService.selectJobLogById(jobLogId)); + } + + + /** + * 删除定时任务调度日志 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:remove')") + @Log(title = "定时任务调度日志", businessType = BusinessType.DELETE) + @DeleteMapping("/{jobLogIds}") + public AjaxResult remove(@PathVariable Long[] jobLogIds) { + return toAjax(jobLogService.deleteJobLogByIds(jobLogIds)); + } + + /** + * 清空定时任务调度日志 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:remove')") + @Log(title = "调度日志", businessType = BusinessType.CLEAN) + @DeleteMapping("/clean") + public AjaxResult clean() { + jobLogService.cleanJobLog(); + return success(); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJob.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJob.java new file mode 100644 index 00000000..1f496959 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJob.java @@ -0,0 +1,171 @@ +package com.ruoyi.quartz.domain; + +import java.util.Date; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.constant.ScheduleConstants; +import com.ruoyi.common.core.domain.BaseEntity; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.quartz.util.CronUtils; + +/** + * 定时任务调度表 sys_job + * + * @author ruoyi + */ +public class SysJob extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 任务ID */ + @Excel(name = "任务序号", cellType = ColumnType.NUMERIC) + private Long jobId; + + /** 任务名称 */ + @Excel(name = "任务名称") + private String jobName; + + /** 任务组名 */ + @Excel(name = "任务组名") + private String jobGroup; + + /** 调用目标字符串 */ + @Excel(name = "调用目标字符串") + private String invokeTarget; + + /** cron执行表达式 */ + @Excel(name = "执行表达式 ") + private String cronExpression; + + /** cron计划策略 */ + @Excel(name = "计划策略 ", readConverterExp = "0=默认,1=立即触发执行,2=触发一次执行,3=不触发立即执行") + private String misfirePolicy = ScheduleConstants.MISFIRE_DEFAULT; + + /** 是否并发执行(0允许 1禁止) */ + @Excel(name = "并发执行", readConverterExp = "0=允许,1=禁止") + private String concurrent; + + /** 任务状态(0正常 1暂停) */ + @Excel(name = "任务状态", readConverterExp = "0=正常,1=暂停") + private String status; + + public Long getJobId() + { + return jobId; + } + + public void setJobId(Long jobId) + { + this.jobId = jobId; + } + + @NotBlank(message = "任务名称不能为空") + @Size(min = 0, max = 64, message = "任务名称不能超过64个字符") + public String getJobName() + { + return jobName; + } + + public void setJobName(String jobName) + { + this.jobName = jobName; + } + + public String getJobGroup() + { + return jobGroup; + } + + public void setJobGroup(String jobGroup) + { + this.jobGroup = jobGroup; + } + + @NotBlank(message = "调用目标字符串不能为空") + @Size(min = 0, max = 500, message = "调用目标字符串长度不能超过500个字符") + public String getInvokeTarget() + { + return invokeTarget; + } + + public void setInvokeTarget(String invokeTarget) + { + this.invokeTarget = invokeTarget; + } + + @NotBlank(message = "Cron执行表达式不能为空") + @Size(min = 0, max = 255, message = "Cron执行表达式不能超过255个字符") + public String getCronExpression() + { + return cronExpression; + } + + public void setCronExpression(String cronExpression) + { + this.cronExpression = cronExpression; + } + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + public Date getNextValidTime() + { + if (StringUtils.isNotEmpty(cronExpression)) + { + return CronUtils.getNextExecution(cronExpression); + } + return null; + } + + public String getMisfirePolicy() + { + return misfirePolicy; + } + + public void setMisfirePolicy(String misfirePolicy) + { + this.misfirePolicy = misfirePolicy; + } + + public String getConcurrent() + { + return concurrent; + } + + public void setConcurrent(String concurrent) + { + this.concurrent = concurrent; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("jobId", getJobId()) + .append("jobName", getJobName()) + .append("jobGroup", getJobGroup()) + .append("cronExpression", getCronExpression()) + .append("nextValidTime", getNextValidTime()) + .append("misfirePolicy", getMisfirePolicy()) + .append("concurrent", getConcurrent()) + .append("status", getStatus()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJobLog.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJobLog.java new file mode 100644 index 00000000..121c0359 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJobLog.java @@ -0,0 +1,155 @@ +package com.ruoyi.quartz.domain; + +import java.util.Date; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 定时任务调度日志表 sys_job_log + * + * @author ruoyi + */ +public class SysJobLog extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** ID */ + @Excel(name = "日志序号") + private Long jobLogId; + + /** 任务名称 */ + @Excel(name = "任务名称") + private String jobName; + + /** 任务组名 */ + @Excel(name = "任务组名") + private String jobGroup; + + /** 调用目标字符串 */ + @Excel(name = "调用目标字符串") + private String invokeTarget; + + /** 日志信息 */ + @Excel(name = "日志信息") + private String jobMessage; + + /** 执行状态(0正常 1失败) */ + @Excel(name = "执行状态", readConverterExp = "0=正常,1=失败") + private String status; + + /** 异常信息 */ + @Excel(name = "异常信息") + private String exceptionInfo; + + /** 开始时间 */ + private Date startTime; + + /** 停止时间 */ + private Date stopTime; + + public Long getJobLogId() + { + return jobLogId; + } + + public void setJobLogId(Long jobLogId) + { + this.jobLogId = jobLogId; + } + + public String getJobName() + { + return jobName; + } + + public void setJobName(String jobName) + { + this.jobName = jobName; + } + + public String getJobGroup() + { + return jobGroup; + } + + public void setJobGroup(String jobGroup) + { + this.jobGroup = jobGroup; + } + + public String getInvokeTarget() + { + return invokeTarget; + } + + public void setInvokeTarget(String invokeTarget) + { + this.invokeTarget = invokeTarget; + } + + public String getJobMessage() + { + return jobMessage; + } + + public void setJobMessage(String jobMessage) + { + this.jobMessage = jobMessage; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getExceptionInfo() + { + return exceptionInfo; + } + + public void setExceptionInfo(String exceptionInfo) + { + this.exceptionInfo = exceptionInfo; + } + + public Date getStartTime() + { + return startTime; + } + + public void setStartTime(Date startTime) + { + this.startTime = startTime; + } + + public Date getStopTime() + { + return stopTime; + } + + public void setStopTime(Date stopTime) + { + this.stopTime = stopTime; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("jobLogId", getJobLogId()) + .append("jobName", getJobName()) + .append("jobGroup", getJobGroup()) + .append("jobMessage", getJobMessage()) + .append("status", getStatus()) + .append("exceptionInfo", getExceptionInfo()) + .append("startTime", getStartTime()) + .append("stopTime", getStopTime()) + .toString(); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/mapper/SysJobLogMapper.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/mapper/SysJobLogMapper.java new file mode 100644 index 00000000..727d9169 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/mapper/SysJobLogMapper.java @@ -0,0 +1,64 @@ +package com.ruoyi.quartz.mapper; + +import java.util.List; +import com.ruoyi.quartz.domain.SysJobLog; + +/** + * 调度任务日志信息 数据层 + * + * @author ruoyi + */ +public interface SysJobLogMapper +{ + /** + * 获取quartz调度器日志的计划任务 + * + * @param jobLog 调度日志信息 + * @return 调度任务日志集合 + */ + public List selectJobLogList(SysJobLog jobLog); + + /** + * 查询所有调度任务日志 + * + * @return 调度任务日志列表 + */ + public List selectJobLogAll(); + + /** + * 通过调度任务日志ID查询调度信息 + * + * @param jobLogId 调度任务日志ID + * @return 调度任务日志对象信息 + */ + public SysJobLog selectJobLogById(Long jobLogId); + + /** + * 新增任务日志 + * + * @param jobLog 调度日志信息 + * @return 结果 + */ + public int insertJobLog(SysJobLog jobLog); + + /** + * 批量删除调度日志信息 + * + * @param logIds 需要删除的数据ID + * @return 结果 + */ + public int deleteJobLogByIds(Long[] logIds); + + /** + * 删除任务日志 + * + * @param jobId 调度日志ID + * @return 结果 + */ + public int deleteJobLogById(Long jobId); + + /** + * 清空任务日志 + */ + public void cleanJobLog(); +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/mapper/SysJobMapper.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/mapper/SysJobMapper.java new file mode 100644 index 00000000..20f45dbb --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/mapper/SysJobMapper.java @@ -0,0 +1,67 @@ +package com.ruoyi.quartz.mapper; + +import java.util.List; +import com.ruoyi.quartz.domain.SysJob; + +/** + * 调度任务信息 数据层 + * + * @author ruoyi + */ +public interface SysJobMapper +{ + /** + * 查询调度任务日志集合 + * + * @param job 调度信息 + * @return 操作日志集合 + */ + public List selectJobList(SysJob job); + + /** + * 查询所有调度任务 + * + * @return 调度任务列表 + */ + public List selectJobAll(); + + /** + * 通过调度ID查询调度任务信息 + * + * @param jobId 调度ID + * @return 角色对象信息 + */ + public SysJob selectJobById(Long jobId); + + /** + * 通过调度ID删除调度任务信息 + * + * @param jobId 调度ID + * @return 结果 + */ + public int deleteJobById(Long jobId); + + /** + * 批量删除调度任务信息 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteJobByIds(Long[] ids); + + /** + * 修改调度任务信息 + * + * @param job 调度任务信息 + * @return 结果 + */ + public int updateJob(SysJob job); + + /** + * 新增调度任务信息 + * + * @param job 调度任务信息 + * @return 结果 + */ + public int insertJob(SysJob job); +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobLogService.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobLogService.java new file mode 100644 index 00000000..8546792a --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobLogService.java @@ -0,0 +1,56 @@ +package com.ruoyi.quartz.service; + +import java.util.List; +import com.ruoyi.quartz.domain.SysJobLog; + +/** + * 定时任务调度日志信息信息 服务层 + * + * @author ruoyi + */ +public interface ISysJobLogService +{ + /** + * 获取quartz调度器日志的计划任务 + * + * @param jobLog 调度日志信息 + * @return 调度任务日志集合 + */ + public List selectJobLogList(SysJobLog jobLog); + + /** + * 通过调度任务日志ID查询调度信息 + * + * @param jobLogId 调度任务日志ID + * @return 调度任务日志对象信息 + */ + public SysJobLog selectJobLogById(Long jobLogId); + + /** + * 新增任务日志 + * + * @param jobLog 调度日志信息 + */ + public void addJobLog(SysJobLog jobLog); + + /** + * 批量删除调度日志信息 + * + * @param logIds 需要删除的日志ID + * @return 结果 + */ + public int deleteJobLogByIds(Long[] logIds); + + /** + * 删除任务日志 + * + * @param jobId 调度日志ID + * @return 结果 + */ + public int deleteJobLogById(Long jobId); + + /** + * 清空任务日志 + */ + public void cleanJobLog(); +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobService.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobService.java new file mode 100644 index 00000000..437ade82 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobService.java @@ -0,0 +1,102 @@ +package com.ruoyi.quartz.service; + +import java.util.List; +import org.quartz.SchedulerException; +import com.ruoyi.common.exception.job.TaskException; +import com.ruoyi.quartz.domain.SysJob; + +/** + * 定时任务调度信息信息 服务层 + * + * @author ruoyi + */ +public interface ISysJobService +{ + /** + * 获取quartz调度器的计划任务 + * + * @param job 调度信息 + * @return 调度任务集合 + */ + public List selectJobList(SysJob job); + + /** + * 通过调度任务ID查询调度信息 + * + * @param jobId 调度任务ID + * @return 调度任务对象信息 + */ + public SysJob selectJobById(Long jobId); + + /** + * 暂停任务 + * + * @param job 调度信息 + * @return 结果 + */ + public int pauseJob(SysJob job) throws SchedulerException; + + /** + * 恢复任务 + * + * @param job 调度信息 + * @return 结果 + */ + public int resumeJob(SysJob job) throws SchedulerException; + + /** + * 删除任务后,所对应的trigger也将被删除 + * + * @param job 调度信息 + * @return 结果 + */ + public int deleteJob(SysJob job) throws SchedulerException; + + /** + * 批量删除调度信息 + * + * @param jobIds 需要删除的任务ID + * @return 结果 + */ + public void deleteJobByIds(Long[] jobIds) throws SchedulerException; + + /** + * 任务调度状态修改 + * + * @param job 调度信息 + * @return 结果 + */ + public int changeStatus(SysJob job) throws SchedulerException; + + /** + * 立即运行任务 + * + * @param job 调度信息 + * @return 结果 + */ + public boolean run(SysJob job) throws SchedulerException; + + /** + * 新增任务 + * + * @param job 调度信息 + * @return 结果 + */ + public int insertJob(SysJob job) throws SchedulerException, TaskException; + + /** + * 更新任务 + * + * @param job 调度信息 + * @return 结果 + */ + public int updateJob(SysJob job) throws SchedulerException, TaskException; + + /** + * 校验cron表达式是否有效 + * + * @param cronExpression 表达式 + * @return 结果 + */ + public boolean checkCronExpressionIsValid(String cronExpression); +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobLogServiceImpl.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobLogServiceImpl.java new file mode 100644 index 00000000..812eed77 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobLogServiceImpl.java @@ -0,0 +1,87 @@ +package com.ruoyi.quartz.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.quartz.domain.SysJobLog; +import com.ruoyi.quartz.mapper.SysJobLogMapper; +import com.ruoyi.quartz.service.ISysJobLogService; + +/** + * 定时任务调度日志信息 服务层 + * + * @author ruoyi + */ +@Service +public class SysJobLogServiceImpl implements ISysJobLogService +{ + @Autowired + private SysJobLogMapper jobLogMapper; + + /** + * 获取quartz调度器日志的计划任务 + * + * @param jobLog 调度日志信息 + * @return 调度任务日志集合 + */ + @Override + public List selectJobLogList(SysJobLog jobLog) + { + return jobLogMapper.selectJobLogList(jobLog); + } + + /** + * 通过调度任务日志ID查询调度信息 + * + * @param jobLogId 调度任务日志ID + * @return 调度任务日志对象信息 + */ + @Override + public SysJobLog selectJobLogById(Long jobLogId) + { + return jobLogMapper.selectJobLogById(jobLogId); + } + + /** + * 新增任务日志 + * + * @param jobLog 调度日志信息 + */ + @Override + public void addJobLog(SysJobLog jobLog) + { + jobLogMapper.insertJobLog(jobLog); + } + + /** + * 批量删除调度日志信息 + * + * @param logIds 需要删除的数据ID + * @return 结果 + */ + @Override + public int deleteJobLogByIds(Long[] logIds) + { + return jobLogMapper.deleteJobLogByIds(logIds); + } + + /** + * 删除任务日志 + * + * @param jobId 调度日志ID + */ + @Override + public int deleteJobLogById(Long jobId) + { + return jobLogMapper.deleteJobLogById(jobId); + } + + /** + * 清空任务日志 + */ + @Override + public void cleanJobLog() + { + jobLogMapper.cleanJobLog(); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobServiceImpl.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobServiceImpl.java new file mode 100644 index 00000000..77fdbb57 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobServiceImpl.java @@ -0,0 +1,261 @@ +package com.ruoyi.quartz.service.impl; + +import java.util.List; +import javax.annotation.PostConstruct; +import org.quartz.JobDataMap; +import org.quartz.JobKey; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import com.ruoyi.common.constant.ScheduleConstants; +import com.ruoyi.common.exception.job.TaskException; +import com.ruoyi.quartz.domain.SysJob; +import com.ruoyi.quartz.mapper.SysJobMapper; +import com.ruoyi.quartz.service.ISysJobService; +import com.ruoyi.quartz.util.CronUtils; +import com.ruoyi.quartz.util.ScheduleUtils; + +/** + * 定时任务调度信息 服务层 + * + * @author ruoyi + */ +@Service +public class SysJobServiceImpl implements ISysJobService +{ + @Autowired + private Scheduler scheduler; + + @Autowired + private SysJobMapper jobMapper; + + /** + * 项目启动时,初始化定时器 主要是防止手动修改数据库导致未同步到定时任务处理(注:不能手动修改数据库ID和任务组名,否则会导致脏数据) + */ + @PostConstruct + public void init() throws SchedulerException, TaskException + { + scheduler.clear(); + List jobList = jobMapper.selectJobAll(); + for (SysJob job : jobList) + { + ScheduleUtils.createScheduleJob(scheduler, job); + } + } + + /** + * 获取quartz调度器的计划任务列表 + * + * @param job 调度信息 + * @return + */ + @Override + public List selectJobList(SysJob job) + { + return jobMapper.selectJobList(job); + } + + /** + * 通过调度任务ID查询调度信息 + * + * @param jobId 调度任务ID + * @return 调度任务对象信息 + */ + @Override + public SysJob selectJobById(Long jobId) + { + return jobMapper.selectJobById(jobId); + } + + /** + * 暂停任务 + * + * @param job 调度信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int pauseJob(SysJob job) throws SchedulerException + { + Long jobId = job.getJobId(); + String jobGroup = job.getJobGroup(); + job.setStatus(ScheduleConstants.Status.PAUSE.getValue()); + int rows = jobMapper.updateJob(job); + if (rows > 0) + { + scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup)); + } + return rows; + } + + /** + * 恢复任务 + * + * @param job 调度信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int resumeJob(SysJob job) throws SchedulerException + { + Long jobId = job.getJobId(); + String jobGroup = job.getJobGroup(); + job.setStatus(ScheduleConstants.Status.NORMAL.getValue()); + int rows = jobMapper.updateJob(job); + if (rows > 0) + { + scheduler.resumeJob(ScheduleUtils.getJobKey(jobId, jobGroup)); + } + return rows; + } + + /** + * 删除任务后,所对应的trigger也将被删除 + * + * @param job 调度信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int deleteJob(SysJob job) throws SchedulerException + { + Long jobId = job.getJobId(); + String jobGroup = job.getJobGroup(); + int rows = jobMapper.deleteJobById(jobId); + if (rows > 0) + { + scheduler.deleteJob(ScheduleUtils.getJobKey(jobId, jobGroup)); + } + return rows; + } + + /** + * 批量删除调度信息 + * + * @param jobIds 需要删除的任务ID + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteJobByIds(Long[] jobIds) throws SchedulerException + { + for (Long jobId : jobIds) + { + SysJob job = jobMapper.selectJobById(jobId); + deleteJob(job); + } + } + + /** + * 任务调度状态修改 + * + * @param job 调度信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int changeStatus(SysJob job) throws SchedulerException + { + int rows = 0; + String status = job.getStatus(); + if (ScheduleConstants.Status.NORMAL.getValue().equals(status)) + { + rows = resumeJob(job); + } + else if (ScheduleConstants.Status.PAUSE.getValue().equals(status)) + { + rows = pauseJob(job); + } + return rows; + } + + /** + * 立即运行任务 + * + * @param job 调度信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean run(SysJob job) throws SchedulerException + { + boolean result = false; + Long jobId = job.getJobId(); + String jobGroup = job.getJobGroup(); + SysJob properties = selectJobById(job.getJobId()); + // 参数 + JobDataMap dataMap = new JobDataMap(); + dataMap.put(ScheduleConstants.TASK_PROPERTIES, properties); + JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup); + if (scheduler.checkExists(jobKey)) + { + result = true; + scheduler.triggerJob(jobKey, dataMap); + } + return result; + } + + /** + * 新增任务 + * + * @param job 调度信息 调度信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int insertJob(SysJob job) throws SchedulerException, TaskException + { + job.setStatus(ScheduleConstants.Status.PAUSE.getValue()); + int rows = jobMapper.insertJob(job); + if (rows > 0) + { + ScheduleUtils.createScheduleJob(scheduler, job); + } + return rows; + } + + /** + * 更新任务的时间表达式 + * + * @param job 调度信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int updateJob(SysJob job) throws SchedulerException, TaskException + { + SysJob properties = selectJobById(job.getJobId()); + int rows = jobMapper.updateJob(job); + if (rows > 0) + { + updateSchedulerJob(job, properties.getJobGroup()); + } + return rows; + } + + /** + * 更新任务 + * + * @param job 任务对象 + * @param jobGroup 任务组名 + */ + public void updateSchedulerJob(SysJob job, String jobGroup) throws SchedulerException, TaskException + { + Long jobId = job.getJobId(); + // 判断是否存在 + JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup); + if (scheduler.checkExists(jobKey)) + { + // 防止创建时存在数据问题 先移除,然后在执行创建操作 + scheduler.deleteJob(jobKey); + } + ScheduleUtils.createScheduleJob(scheduler, job); + } + + /** + * 校验cron表达式是否有效 + * + * @param cronExpression 表达式 + * @return 结果 + */ + @Override + public boolean checkCronExpressionIsValid(String cronExpression) + { + return CronUtils.isValid(cronExpression); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/CommonTask.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/CommonTask.java new file mode 100644 index 00000000..f47aa604 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/CommonTask.java @@ -0,0 +1,30 @@ +package com.ruoyi.quartz.task; + +import com.ruoyi.common.config.RuoYiConfig; +import org.apache.commons.io.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.io.IOException; + +/** + * 定时任务调度测试 + * + * @author ruoyi + */ +@Component("commonTask") +public class CommonTask { + protected final Logger logger = LoggerFactory.getLogger(this.getClass()); + + public void deleteTempFile() { + logger.info("开始删除文件"); + String downloadPath = RuoYiConfig.getTempDownloadPath(); + try { + FileUtils.deleteDirectory(new File(downloadPath)); + } catch (IOException e) { + logger.error("删除download tempfile文件夹失败" + e.getMessage(), e); + } + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/AbstractQuartzJob.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/AbstractQuartzJob.java new file mode 100644 index 00000000..731a5eb5 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/AbstractQuartzJob.java @@ -0,0 +1,107 @@ +package com.ruoyi.quartz.util; + +import java.util.Date; +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.ScheduleConstants; +import com.ruoyi.common.utils.ExceptionUtil; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.bean.BeanUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.quartz.domain.SysJob; +import com.ruoyi.quartz.domain.SysJobLog; +import com.ruoyi.quartz.service.ISysJobLogService; + +/** + * 抽象quartz调用 + * + * @author ruoyi + */ +public abstract class AbstractQuartzJob implements Job +{ + private static final Logger log = LoggerFactory.getLogger(AbstractQuartzJob.class); + + /** + * 线程本地变量 + */ + private static ThreadLocal threadLocal = new ThreadLocal<>(); + + @Override + public void execute(JobExecutionContext context) throws JobExecutionException + { + SysJob sysJob = new SysJob(); + BeanUtils.copyBeanProp(sysJob, context.getMergedJobDataMap().get(ScheduleConstants.TASK_PROPERTIES)); + try + { + before(context, sysJob); + if (sysJob != null) + { + doExecute(context, sysJob); + } + after(context, sysJob, null); + } + catch (Exception e) + { + log.error("任务执行异常 - :", e); + after(context, sysJob, e); + } + } + + /** + * 执行前 + * + * @param context 工作执行上下文对象 + * @param sysJob 系统计划任务 + */ + protected void before(JobExecutionContext context, SysJob sysJob) + { + threadLocal.set(new Date()); + } + + /** + * 执行后 + * + * @param context 工作执行上下文对象 + * @param sysJob 系统计划任务 + */ + protected void after(JobExecutionContext context, SysJob sysJob, Exception e) + { + Date startTime = threadLocal.get(); + threadLocal.remove(); + + final SysJobLog sysJobLog = new SysJobLog(); + sysJobLog.setJobName(sysJob.getJobName()); + sysJobLog.setJobGroup(sysJob.getJobGroup()); + sysJobLog.setInvokeTarget(sysJob.getInvokeTarget()); + sysJobLog.setStartTime(startTime); + sysJobLog.setStopTime(new Date()); + long runMs = sysJobLog.getStopTime().getTime() - sysJobLog.getStartTime().getTime(); + sysJobLog.setJobMessage(sysJobLog.getJobName() + " 总共耗时:" + runMs + "毫秒"); + if (e != null) + { + sysJobLog.setStatus(Constants.FAIL); + String errorMsg = StringUtils.substring(ExceptionUtil.getExceptionMessage(e), 0, 2000); + sysJobLog.setExceptionInfo(errorMsg); + } + else + { + sysJobLog.setStatus(Constants.SUCCESS); + } + + // 写入数据库当中 + SpringUtils.getBean(ISysJobLogService.class).addJobLog(sysJobLog); + } + + /** + * 执行方法,由子类重载 + * + * @param context 工作执行上下文对象 + * @param sysJob 系统计划任务 + * @throws Exception 执行过程中的异常 + */ + protected abstract void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception; +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/CronUtils.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/CronUtils.java new file mode 100644 index 00000000..dd538397 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/CronUtils.java @@ -0,0 +1,63 @@ +package com.ruoyi.quartz.util; + +import java.text.ParseException; +import java.util.Date; +import org.quartz.CronExpression; + +/** + * cron表达式工具类 + * + * @author ruoyi + * + */ +public class CronUtils +{ + /** + * 返回一个布尔值代表一个给定的Cron表达式的有效性 + * + * @param cronExpression Cron表达式 + * @return boolean 表达式是否有效 + */ + public static boolean isValid(String cronExpression) + { + return CronExpression.isValidExpression(cronExpression); + } + + /** + * 返回一个字符串值,表示该消息无效Cron表达式给出有效性 + * + * @param cronExpression Cron表达式 + * @return String 无效时返回表达式错误描述,如果有效返回null + */ + public static String getInvalidMessage(String cronExpression) + { + try + { + new CronExpression(cronExpression); + return null; + } + catch (ParseException pe) + { + return pe.getMessage(); + } + } + + /** + * 返回下一个执行时间根据给定的Cron表达式 + * + * @param cronExpression Cron表达式 + * @return Date 下次Cron表达式执行时间 + */ + public static Date getNextExecution(String cronExpression) + { + try + { + CronExpression cron = new CronExpression(cronExpression); + return cron.getNextValidTimeAfter(new Date(System.currentTimeMillis())); + } + catch (ParseException e) + { + throw new IllegalArgumentException(e.getMessage()); + } + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/JobInvokeUtil.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/JobInvokeUtil.java new file mode 100644 index 00000000..4f7de72b --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/JobInvokeUtil.java @@ -0,0 +1,182 @@ +package com.ruoyi.quartz.util; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.LinkedList; +import java.util.List; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.quartz.domain.SysJob; + +/** + * 任务执行工具 + * + * @author ruoyi + */ +public class JobInvokeUtil +{ + /** + * 执行方法 + * + * @param sysJob 系统任务 + */ + public static void invokeMethod(SysJob sysJob) throws Exception + { + String invokeTarget = sysJob.getInvokeTarget(); + String beanName = getBeanName(invokeTarget); + String methodName = getMethodName(invokeTarget); + List methodParams = getMethodParams(invokeTarget); + + if (!isValidClassName(beanName)) + { + Object bean = SpringUtils.getBean(beanName); + invokeMethod(bean, methodName, methodParams); + } + else + { + Object bean = Class.forName(beanName).newInstance(); + invokeMethod(bean, methodName, methodParams); + } + } + + /** + * 调用任务方法 + * + * @param bean 目标对象 + * @param methodName 方法名称 + * @param methodParams 方法参数 + */ + private static void invokeMethod(Object bean, String methodName, List methodParams) + throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, + InvocationTargetException + { + if (StringUtils.isNotNull(methodParams) && methodParams.size() > 0) + { + Method method = bean.getClass().getMethod(methodName, getMethodParamsType(methodParams)); + method.invoke(bean, getMethodParamsValue(methodParams)); + } + else + { + Method method = bean.getClass().getMethod(methodName); + method.invoke(bean); + } + } + + /** + * 校验是否为为class包名 + * + * @param invokeTarget 名称 + * @return true是 false否 + */ + public static boolean isValidClassName(String invokeTarget) + { + return StringUtils.countMatches(invokeTarget, ".") > 1; + } + + /** + * 获取bean名称 + * + * @param invokeTarget 目标字符串 + * @return bean名称 + */ + public static String getBeanName(String invokeTarget) + { + String beanName = StringUtils.substringBefore(invokeTarget, "("); + return StringUtils.substringBeforeLast(beanName, "."); + } + + /** + * 获取bean方法 + * + * @param invokeTarget 目标字符串 + * @return method方法 + */ + public static String getMethodName(String invokeTarget) + { + String methodName = StringUtils.substringBefore(invokeTarget, "("); + return StringUtils.substringAfterLast(methodName, "."); + } + + /** + * 获取method方法参数相关列表 + * + * @param invokeTarget 目标字符串 + * @return method方法相关参数列表 + */ + public static List getMethodParams(String invokeTarget) + { + String methodStr = StringUtils.substringBetween(invokeTarget, "(", ")"); + if (StringUtils.isEmpty(methodStr)) + { + return null; + } + String[] methodParams = methodStr.split(",(?=([^\"']*[\"'][^\"']*[\"'])*[^\"']*$)"); + List classs = new LinkedList<>(); + for (int i = 0; i < methodParams.length; i++) + { + String str = StringUtils.trimToEmpty(methodParams[i]); + // String字符串类型,以'或"开头 + if (StringUtils.startsWithAny(str, "'", "\"")) + { + classs.add(new Object[] { StringUtils.substring(str, 1, str.length() - 1), String.class }); + } + // boolean布尔类型,等于true或者false + else if ("true".equalsIgnoreCase(str) || "false".equalsIgnoreCase(str)) + { + classs.add(new Object[] { Boolean.valueOf(str), Boolean.class }); + } + // long长整形,以L结尾 + else if (StringUtils.endsWith(str, "L")) + { + classs.add(new Object[] { Long.valueOf(StringUtils.substring(str, 0, str.length() - 1)), Long.class }); + } + // double浮点类型,以D结尾 + else if (StringUtils.endsWith(str, "D")) + { + classs.add(new Object[] { Double.valueOf(StringUtils.substring(str, 0, str.length() - 1)), Double.class }); + } + // 其他类型归类为整形 + else + { + classs.add(new Object[] { Integer.valueOf(str), Integer.class }); + } + } + return classs; + } + + /** + * 获取参数类型 + * + * @param methodParams 参数相关列表 + * @return 参数类型列表 + */ + public static Class[] getMethodParamsType(List methodParams) + { + Class[] classs = new Class[methodParams.size()]; + int index = 0; + for (Object[] os : methodParams) + { + classs[index] = (Class) os[1]; + index++; + } + return classs; + } + + /** + * 获取参数值 + * + * @param methodParams 参数相关列表 + * @return 参数值列表 + */ + public static Object[] getMethodParamsValue(List methodParams) + { + Object[] classs = new Object[methodParams.size()]; + int index = 0; + for (Object[] os : methodParams) + { + classs[index] = (Object) os[0]; + index++; + } + return classs; + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzDisallowConcurrentExecution.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzDisallowConcurrentExecution.java new file mode 100644 index 00000000..5e135580 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzDisallowConcurrentExecution.java @@ -0,0 +1,21 @@ +package com.ruoyi.quartz.util; + +import org.quartz.DisallowConcurrentExecution; +import org.quartz.JobExecutionContext; +import com.ruoyi.quartz.domain.SysJob; + +/** + * 定时任务处理(禁止并发执行) + * + * @author ruoyi + * + */ +@DisallowConcurrentExecution +public class QuartzDisallowConcurrentExecution extends AbstractQuartzJob +{ + @Override + protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception + { + JobInvokeUtil.invokeMethod(sysJob); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzJobExecution.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzJobExecution.java new file mode 100644 index 00000000..e9753261 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzJobExecution.java @@ -0,0 +1,19 @@ +package com.ruoyi.quartz.util; + +import org.quartz.JobExecutionContext; +import com.ruoyi.quartz.domain.SysJob; + +/** + * 定时任务处理(允许并发执行) + * + * @author ruoyi + * + */ +public class QuartzJobExecution extends AbstractQuartzJob +{ + @Override + protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception + { + JobInvokeUtil.invokeMethod(sysJob); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/ScheduleUtils.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/ScheduleUtils.java new file mode 100644 index 00000000..f885d422 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/ScheduleUtils.java @@ -0,0 +1,139 @@ +package com.ruoyi.quartz.util; + +import org.quartz.CronScheduleBuilder; +import org.quartz.CronTrigger; +import org.quartz.Job; +import org.quartz.JobBuilder; +import org.quartz.JobDetail; +import org.quartz.JobKey; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.TriggerBuilder; +import org.quartz.TriggerKey; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.ScheduleConstants; +import com.ruoyi.common.exception.job.TaskException; +import com.ruoyi.common.exception.job.TaskException.Code; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.quartz.domain.SysJob; + +/** + * 定时任务工具类 + * + * @author ruoyi + * + */ +public class ScheduleUtils +{ + /** + * 得到quartz任务类 + * + * @param sysJob 执行计划 + * @return 具体执行任务类 + */ + private static Class getQuartzJobClass(SysJob sysJob) + { + boolean isConcurrent = "0".equals(sysJob.getConcurrent()); + return isConcurrent ? QuartzJobExecution.class : QuartzDisallowConcurrentExecution.class; + } + + /** + * 构建任务触发对象 + */ + public static TriggerKey getTriggerKey(Long jobId, String jobGroup) + { + return TriggerKey.triggerKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup); + } + + /** + * 构建任务键对象 + */ + public static JobKey getJobKey(Long jobId, String jobGroup) + { + return JobKey.jobKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup); + } + + /** + * 创建定时任务 + */ + public static void createScheduleJob(Scheduler scheduler, SysJob job) throws SchedulerException, TaskException + { + Class jobClass = getQuartzJobClass(job); + // 构建job信息 + Long jobId = job.getJobId(); + String jobGroup = job.getJobGroup(); + JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(getJobKey(jobId, jobGroup)).build(); + + // 表达式调度构建器 + CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression()); + cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder); + + // 按新的cronExpression表达式构建一个新的trigger + CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(jobId, jobGroup)) + .withSchedule(cronScheduleBuilder).build(); + + // 放入参数,运行时的方法可以获取 + jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job); + + // 判断是否存在 + if (scheduler.checkExists(getJobKey(jobId, jobGroup))) + { + // 防止创建时存在数据问题 先移除,然后在执行创建操作 + scheduler.deleteJob(getJobKey(jobId, jobGroup)); + } + + // 判断任务是否过期 + if (StringUtils.isNotNull(CronUtils.getNextExecution(job.getCronExpression()))) + { + // 执行调度任务 + scheduler.scheduleJob(jobDetail, trigger); + } + + // 暂停任务 + if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue())) + { + scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup)); + } + } + + /** + * 设置定时任务策略 + */ + public static CronScheduleBuilder handleCronScheduleMisfirePolicy(SysJob job, CronScheduleBuilder cb) + throws TaskException + { + switch (job.getMisfirePolicy()) + { + case ScheduleConstants.MISFIRE_DEFAULT: + return cb; + case ScheduleConstants.MISFIRE_IGNORE_MISFIRES: + return cb.withMisfireHandlingInstructionIgnoreMisfires(); + case ScheduleConstants.MISFIRE_FIRE_AND_PROCEED: + return cb.withMisfireHandlingInstructionFireAndProceed(); + case ScheduleConstants.MISFIRE_DO_NOTHING: + return cb.withMisfireHandlingInstructionDoNothing(); + default: + throw new TaskException("The task misfire policy '" + job.getMisfirePolicy() + + "' cannot be used in cron schedule tasks", Code.CONFIG_ERROR); + } + } + + /** + * 检查包名是否为白名单配置 + * + * @param invokeTarget 目标字符串 + * @return 结果 + */ + public static boolean whiteList(String invokeTarget) + { + String packageName = StringUtils.substringBefore(invokeTarget, "("); + int count = StringUtils.countMatches(packageName, "."); + if (count > 1) + { + return StringUtils.containsAnyIgnoreCase(invokeTarget, Constants.JOB_WHITELIST_STR); + } + Object obj = SpringUtils.getBean(StringUtils.split(invokeTarget, ".")[0]); + return StringUtils.containsAnyIgnoreCase(obj.getClass().getPackage().getName(), Constants.JOB_WHITELIST_STR); + } +} diff --git a/ruoyi-quartz/src/main/resources/mapper/quartz/SysJobLogMapper.xml b/ruoyi-quartz/src/main/resources/mapper/quartz/SysJobLogMapper.xml new file mode 100644 index 00000000..e608e428 --- /dev/null +++ b/ruoyi-quartz/src/main/resources/mapper/quartz/SysJobLogMapper.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + select job_log_id, job_name, job_group, invoke_target, job_message, status, exception_info, create_time + from sys_job_log + + + + + + + + + + delete from sys_job_log where job_log_id = #{jobLogId} + + + + delete from sys_job_log where job_log_id in + + #{jobLogId} + + + + + truncate table sys_job_log + + + + insert into sys_job_log( + job_log_id, + job_name, + job_group, + invoke_target, + job_message, + status, + exception_info, + create_time + )values( + #{jobLogId}, + #{jobName}, + #{jobGroup}, + #{invokeTarget}, + #{jobMessage}, + #{status}, + #{exceptionInfo}, + sysdate() + ) + + + \ No newline at end of file diff --git a/ruoyi-quartz/src/main/resources/mapper/quartz/SysJobMapper.xml b/ruoyi-quartz/src/main/resources/mapper/quartz/SysJobMapper.xml new file mode 100644 index 00000000..5605c444 --- /dev/null +++ b/ruoyi-quartz/src/main/resources/mapper/quartz/SysJobMapper.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + select job_id, job_name, job_group, invoke_target, cron_expression, misfire_policy, concurrent, status, create_by, create_time, remark + from sys_job + + + + + + + + + + delete from sys_job where job_id = #{jobId} + + + + delete from sys_job where job_id in + + #{jobId} + + + + + update sys_job + + job_name = #{jobName}, + job_group = #{jobGroup}, + invoke_target = #{invokeTarget}, + cron_expression = #{cronExpression}, + misfire_policy = #{misfirePolicy}, + concurrent = #{concurrent}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where job_id = #{jobId} + + + + insert into sys_job( + job_id, + job_name, + job_group, + invoke_target, + cron_expression, + misfire_policy, + concurrent, + status, + remark, + create_by, + create_time + )values( + #{jobId}, + #{jobName}, + #{jobGroup}, + #{invokeTarget}, + #{cronExpression}, + #{misfirePolicy}, + #{concurrent}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + \ No newline at end of file diff --git a/ruoyi-quartz/target/classes/com/ruoyi/quartz/controller/SysJobController.class b/ruoyi-quartz/target/classes/com/ruoyi/quartz/controller/SysJobController.class new file mode 100644 index 00000000..5fd2ef7d Binary files /dev/null and b/ruoyi-quartz/target/classes/com/ruoyi/quartz/controller/SysJobController.class differ diff --git a/ruoyi-quartz/target/classes/com/ruoyi/quartz/controller/SysJobLogController.class b/ruoyi-quartz/target/classes/com/ruoyi/quartz/controller/SysJobLogController.class new file mode 100644 index 00000000..90a44df9 Binary files /dev/null and b/ruoyi-quartz/target/classes/com/ruoyi/quartz/controller/SysJobLogController.class differ diff --git a/ruoyi-quartz/target/classes/com/ruoyi/quartz/domain/SysJob.class b/ruoyi-quartz/target/classes/com/ruoyi/quartz/domain/SysJob.class new file mode 100644 index 00000000..3c5544ba Binary files /dev/null and b/ruoyi-quartz/target/classes/com/ruoyi/quartz/domain/SysJob.class differ diff --git a/ruoyi-quartz/target/classes/com/ruoyi/quartz/domain/SysJobLog.class b/ruoyi-quartz/target/classes/com/ruoyi/quartz/domain/SysJobLog.class new file mode 100644 index 00000000..e12cc0d0 Binary files /dev/null and b/ruoyi-quartz/target/classes/com/ruoyi/quartz/domain/SysJobLog.class differ diff --git a/ruoyi-quartz/target/classes/com/ruoyi/quartz/mapper/SysJobLogMapper.class b/ruoyi-quartz/target/classes/com/ruoyi/quartz/mapper/SysJobLogMapper.class new file mode 100644 index 00000000..adb6134a Binary files /dev/null and b/ruoyi-quartz/target/classes/com/ruoyi/quartz/mapper/SysJobLogMapper.class differ diff --git a/ruoyi-quartz/target/classes/com/ruoyi/quartz/mapper/SysJobMapper.class b/ruoyi-quartz/target/classes/com/ruoyi/quartz/mapper/SysJobMapper.class new file mode 100644 index 00000000..db85482a Binary files /dev/null and b/ruoyi-quartz/target/classes/com/ruoyi/quartz/mapper/SysJobMapper.class differ diff --git a/ruoyi-quartz/target/classes/com/ruoyi/quartz/service/ISysJobLogService.class b/ruoyi-quartz/target/classes/com/ruoyi/quartz/service/ISysJobLogService.class new file mode 100644 index 00000000..0cc728c4 Binary files /dev/null and b/ruoyi-quartz/target/classes/com/ruoyi/quartz/service/ISysJobLogService.class differ diff --git a/ruoyi-quartz/target/classes/com/ruoyi/quartz/service/ISysJobService.class b/ruoyi-quartz/target/classes/com/ruoyi/quartz/service/ISysJobService.class new file mode 100644 index 00000000..e016a8f3 Binary files /dev/null and b/ruoyi-quartz/target/classes/com/ruoyi/quartz/service/ISysJobService.class differ diff --git a/ruoyi-quartz/target/classes/com/ruoyi/quartz/service/impl/SysJobLogServiceImpl.class b/ruoyi-quartz/target/classes/com/ruoyi/quartz/service/impl/SysJobLogServiceImpl.class new file mode 100644 index 00000000..617df9de Binary files /dev/null and b/ruoyi-quartz/target/classes/com/ruoyi/quartz/service/impl/SysJobLogServiceImpl.class differ diff --git a/ruoyi-quartz/target/classes/com/ruoyi/quartz/service/impl/SysJobServiceImpl.class b/ruoyi-quartz/target/classes/com/ruoyi/quartz/service/impl/SysJobServiceImpl.class new file mode 100644 index 00000000..f5d9763a Binary files /dev/null and b/ruoyi-quartz/target/classes/com/ruoyi/quartz/service/impl/SysJobServiceImpl.class differ diff --git a/ruoyi-quartz/target/classes/com/ruoyi/quartz/task/CommonTask.class b/ruoyi-quartz/target/classes/com/ruoyi/quartz/task/CommonTask.class new file mode 100644 index 00000000..df5511de Binary files /dev/null and b/ruoyi-quartz/target/classes/com/ruoyi/quartz/task/CommonTask.class differ diff --git a/ruoyi-quartz/target/classes/com/ruoyi/quartz/util/AbstractQuartzJob.class b/ruoyi-quartz/target/classes/com/ruoyi/quartz/util/AbstractQuartzJob.class new file mode 100644 index 00000000..e9355385 Binary files /dev/null and b/ruoyi-quartz/target/classes/com/ruoyi/quartz/util/AbstractQuartzJob.class differ diff --git a/ruoyi-quartz/target/classes/com/ruoyi/quartz/util/CronUtils.class b/ruoyi-quartz/target/classes/com/ruoyi/quartz/util/CronUtils.class new file mode 100644 index 00000000..de14f5c1 Binary files /dev/null and b/ruoyi-quartz/target/classes/com/ruoyi/quartz/util/CronUtils.class differ diff --git a/ruoyi-quartz/target/classes/com/ruoyi/quartz/util/JobInvokeUtil.class b/ruoyi-quartz/target/classes/com/ruoyi/quartz/util/JobInvokeUtil.class new file mode 100644 index 00000000..e8684695 Binary files /dev/null and b/ruoyi-quartz/target/classes/com/ruoyi/quartz/util/JobInvokeUtil.class differ diff --git a/ruoyi-quartz/target/classes/com/ruoyi/quartz/util/QuartzDisallowConcurrentExecution.class b/ruoyi-quartz/target/classes/com/ruoyi/quartz/util/QuartzDisallowConcurrentExecution.class new file mode 100644 index 00000000..a749705c Binary files /dev/null and b/ruoyi-quartz/target/classes/com/ruoyi/quartz/util/QuartzDisallowConcurrentExecution.class differ diff --git a/ruoyi-quartz/target/classes/com/ruoyi/quartz/util/QuartzJobExecution.class b/ruoyi-quartz/target/classes/com/ruoyi/quartz/util/QuartzJobExecution.class new file mode 100644 index 00000000..1f4bf9cd Binary files /dev/null and b/ruoyi-quartz/target/classes/com/ruoyi/quartz/util/QuartzJobExecution.class differ diff --git a/ruoyi-quartz/target/classes/com/ruoyi/quartz/util/ScheduleUtils.class b/ruoyi-quartz/target/classes/com/ruoyi/quartz/util/ScheduleUtils.class new file mode 100644 index 00000000..3e507cc3 Binary files /dev/null and b/ruoyi-quartz/target/classes/com/ruoyi/quartz/util/ScheduleUtils.class differ diff --git a/ruoyi-quartz/target/classes/mapper/quartz/SysJobLogMapper.xml b/ruoyi-quartz/target/classes/mapper/quartz/SysJobLogMapper.xml new file mode 100644 index 00000000..e608e428 --- /dev/null +++ b/ruoyi-quartz/target/classes/mapper/quartz/SysJobLogMapper.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + select job_log_id, job_name, job_group, invoke_target, job_message, status, exception_info, create_time + from sys_job_log + + + + + + + + + + delete from sys_job_log where job_log_id = #{jobLogId} + + + + delete from sys_job_log where job_log_id in + + #{jobLogId} + + + + + truncate table sys_job_log + + + + insert into sys_job_log( + job_log_id, + job_name, + job_group, + invoke_target, + job_message, + status, + exception_info, + create_time + )values( + #{jobLogId}, + #{jobName}, + #{jobGroup}, + #{invokeTarget}, + #{jobMessage}, + #{status}, + #{exceptionInfo}, + sysdate() + ) + + + \ No newline at end of file diff --git a/ruoyi-quartz/target/classes/mapper/quartz/SysJobMapper.xml b/ruoyi-quartz/target/classes/mapper/quartz/SysJobMapper.xml new file mode 100644 index 00000000..5605c444 --- /dev/null +++ b/ruoyi-quartz/target/classes/mapper/quartz/SysJobMapper.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + select job_id, job_name, job_group, invoke_target, cron_expression, misfire_policy, concurrent, status, create_by, create_time, remark + from sys_job + + + + + + + + + + delete from sys_job where job_id = #{jobId} + + + + delete from sys_job where job_id in + + #{jobId} + + + + + update sys_job + + job_name = #{jobName}, + job_group = #{jobGroup}, + invoke_target = #{invokeTarget}, + cron_expression = #{cronExpression}, + misfire_policy = #{misfirePolicy}, + concurrent = #{concurrent}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where job_id = #{jobId} + + + + insert into sys_job( + job_id, + job_name, + job_group, + invoke_target, + cron_expression, + misfire_policy, + concurrent, + status, + remark, + create_by, + create_time + )values( + #{jobId}, + #{jobName}, + #{jobGroup}, + #{invokeTarget}, + #{cronExpression}, + #{misfirePolicy}, + #{concurrent}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + \ No newline at end of file diff --git a/ruoyi-system/pom.xml b/ruoyi-system/pom.xml new file mode 100644 index 00000000..d59edb83 --- /dev/null +++ b/ruoyi-system/pom.xml @@ -0,0 +1,41 @@ + + + + ruoyi + com.ruoyi + 3.8.4 + + 4.0.0 + + ruoyi-system + + + system系统模块 + + + + + + org.jeecgframework.jimureport + jimureport-spring-boot-starter + + + + com.ruoyi + ruoyi-common + + + + org.projectlombok + lombok + true + + + com.ruoyi + ruoyi-springboot-starter-tenant + + + + diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/convert/Device/DeviceConvert.java b/ruoyi-system/src/main/java/com/ruoyi/system/convert/Device/DeviceConvert.java new file mode 100644 index 00000000..dec38dde --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/convert/Device/DeviceConvert.java @@ -0,0 +1,17 @@ +package com.ruoyi.system.convert.Device; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface DeviceConvert { + DeviceConvert INSTANCE = Mappers.getMapper(DeviceConvert.class); +// @Mappings({ +// @Mapping(source = "deviceId", target = "id"), +// @Mapping(source = "deviceName", target = "name") +// }) +// NameIdVo bsDeviceToNameIdVo(BsDevice bsDevice); + +// List bsDeviceListToNameIdVoList(List bsDeviceList); +} + diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/convert/task/TaskConvert.java b/ruoyi-system/src/main/java/com/ruoyi/system/convert/task/TaskConvert.java new file mode 100644 index 00000000..fd82d952 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/convert/task/TaskConvert.java @@ -0,0 +1,27 @@ +package com.ruoyi.system.convert.task; + +//import com.ruoyi.system.domain.BsTask; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface TaskConvert { + + TaskConvert INSTANCE = Mappers.getMapper(TaskConvert.class); + + +// BsTask pointResqToTask(PointResqVo vo); + +// BsTask settingUserInfoResqToTask(SettingUserInfoResqVo vo); + +// BsTask batchSettingResqVoToTask(BatchSettingResqVo vo); + +// @Mappings({ +// @Mapping(source = "taskId", target = "id"), +// @Mapping(source = "taskName", target = "name") +// }) +// NameIdVo taskToNameIdVo(BsTask task); +// +// List taskListToNameIdVoList(List taskList); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/convert/tenant/TenantConvert.java b/ruoyi-system/src/main/java/com/ruoyi/system/convert/tenant/TenantConvert.java new file mode 100644 index 00000000..b40196b3 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/convert/tenant/TenantConvert.java @@ -0,0 +1,47 @@ +package com.ruoyi.system.convert.tenant; + + +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.system.domain.TenantDO; +import com.ruoyi.system.domain.vo.tenant.TenantCreateReqVO; +import com.ruoyi.system.domain.vo.tenant.TenantExcelVO; +import com.ruoyi.system.domain.vo.tenant.TenantRespVO; +import com.ruoyi.system.domain.vo.tenant.TenantUpdateReqVO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +/** + * 租户 Convert + * + * @author 芋道源码 + */ +@Mapper +public interface TenantConvert { + + TenantConvert INSTANCE = Mappers.getMapper(TenantConvert.class); + + TenantDO convert(TenantCreateReqVO bean); + + TenantDO convert(TenantUpdateReqVO bean); + + TenantRespVO convert(TenantDO bean); + + List convertList(List list); + + PageResult convertPage(PageResult page); + + List convertList02(List list); + + default SysUser convert02(TenantCreateReqVO bean) { + SysUser reqVO = new SysUser(); + reqVO.setUserName(bean.getUsername()); + reqVO.setPassword(bean.getPassword()); + reqVO.setNickName(bean.getContactName()); + reqVO.setPhonenumber(bean.getContactMobile()); + return reqVO; + } + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/convert/tenant/TenantPackageConvert.java b/ruoyi-system/src/main/java/com/ruoyi/system/convert/tenant/TenantPackageConvert.java new file mode 100644 index 00000000..167f721c --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/convert/tenant/TenantPackageConvert.java @@ -0,0 +1,37 @@ +package com.ruoyi.system.convert.tenant; + + +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.system.domain.TenantPackageDO; +import com.ruoyi.system.domain.vo.packages.TenantPackageCreateReqVO; +import com.ruoyi.system.domain.vo.packages.TenantPackageRespVO; +import com.ruoyi.system.domain.vo.packages.TenantPackageSimpleRespVO; +import com.ruoyi.system.domain.vo.packages.TenantPackageUpdateReqVO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +/** + * 租户套餐 Convert + * + * @author 芋道源码 + */ +@Mapper +public interface TenantPackageConvert { + + TenantPackageConvert INSTANCE = Mappers.getMapper(TenantPackageConvert.class); + + TenantPackageDO convert(TenantPackageCreateReqVO bean); + + TenantPackageDO convert(TenantPackageUpdateReqVO bean); + + TenantPackageRespVO convert(TenantPackageDO bean); + + List convertList(List list); + + PageResult convertPage(PageResult page); + + List convertList02(List list); + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/convert/user/UserConvert.java b/ruoyi-system/src/main/java/com/ruoyi/system/convert/user/UserConvert.java new file mode 100644 index 00000000..bf2213b6 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/convert/user/UserConvert.java @@ -0,0 +1,19 @@ +package com.ruoyi.system.convert.user; + +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.system.domain.vo.user.UserSimpleRespVO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +@Mapper +public interface UserConvert { + + UserConvert INSTANCE = Mappers.getMapper(UserConvert.class); + + + List convertList04(List list); + + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysCache.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysCache.java new file mode 100644 index 00000000..83f0703a --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysCache.java @@ -0,0 +1,81 @@ +package com.ruoyi.system.domain; + +import com.ruoyi.common.utils.StringUtils; + +/** + * 缓存信息 + * + * @author ruoyi + */ +public class SysCache +{ + /** 缓存名称 */ + private String cacheName = ""; + + /** 缓存键名 */ + private String cacheKey = ""; + + /** 缓存内容 */ + private String cacheValue = ""; + + /** 备注 */ + private String remark = ""; + + public SysCache() + { + + } + + public SysCache(String cacheName, String remark) + { + this.cacheName = cacheName; + this.remark = remark; + } + + public SysCache(String cacheName, String cacheKey, String cacheValue) + { + this.cacheName = StringUtils.replace(cacheName, ":", ""); + this.cacheKey = StringUtils.replace(cacheKey, cacheName, ""); + this.cacheValue = cacheValue; + } + + public String getCacheName() + { + return cacheName; + } + + public void setCacheName(String cacheName) + { + this.cacheName = cacheName; + } + + public String getCacheKey() + { + return cacheKey; + } + + public void setCacheKey(String cacheKey) + { + this.cacheKey = cacheKey; + } + + public String getCacheValue() + { + return cacheValue; + } + + public void setCacheValue(String cacheValue) + { + this.cacheValue = cacheValue; + } + + public String getRemark() + { + return remark; + } + + public void setRemark(String remark) + { + this.remark = remark; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysConfig.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysConfig.java new file mode 100644 index 00000000..b87e9022 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysConfig.java @@ -0,0 +1,116 @@ +package com.ruoyi.system.domain; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 参数配置表 sys_config + * + * @author ruoyi + */ +public class SysConfig extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 参数主键 */ + @Excel(name = "参数主键", cellType = ColumnType.NUMERIC) + @TableId(type = IdType.AUTO) + private Long configId; + + /** 参数名称 */ + @Excel(name = "参数名称") + private String configName; + + /** 参数键名 */ + @Excel(name = "参数键名") + private String configKey; + + /** 参数键值 */ + @Excel(name = "参数键值") + private String configValue; + + /** 系统内置(Y是 N否) */ + @Excel(name = "系统内置", readConverterExp = "Y=是,N=否") + private String configType; + + public Long getConfigId() + { + return configId; + } + + public void setConfigId(Long configId) + { + this.configId = configId; + } + + @NotBlank(message = "参数名称不能为空") + @Size(min = 0, max = 100, message = "参数名称不能超过100个字符") + public String getConfigName() + { + return configName; + } + + public void setConfigName(String configName) + { + this.configName = configName; + } + + @NotBlank(message = "参数键名长度不能为空") + @Size(min = 0, max = 100, message = "参数键名长度不能超过100个字符") + public String getConfigKey() + { + return configKey; + } + + public void setConfigKey(String configKey) + { + this.configKey = configKey; + } + + @NotBlank(message = "参数键值不能为空") + @Size(min = 0, max = 500, message = "参数键值长度不能超过500个字符") + public String getConfigValue() + { + return configValue; + } + + public void setConfigValue(String configValue) + { + this.configValue = configValue; + } + + public String getConfigType() + { + return configType; + } + + public void setConfigType(String configType) + { + this.configType = configType; + } + + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("configId", getConfigId()) + .append("configName", getConfigName()) + .append("configKey", getConfigKey()) + .append("configValue", getConfigValue()) + .append("configType", getConfigType()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysLogininfor.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysLogininfor.java new file mode 100644 index 00000000..be46a28b --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysLogininfor.java @@ -0,0 +1,159 @@ +package com.ruoyi.system.domain; + +import java.util.Date; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 系统访问记录表 sys_logininfor + * + * @author ruoyi + */ +public class SysLogininfor extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** ID */ + @Excel(name = "序号", cellType = ColumnType.NUMERIC) + @TableId(type = IdType.AUTO) + private Long infoId; + + /** 用户账号 */ + @Excel(name = "用户账号") + private String userName; + + /** 登录状态 0成功 1失败 */ + @Excel(name = "登录状态", readConverterExp = "0=成功,1=失败") + private String status; + + /** 登录IP地址 */ + @Excel(name = "登录地址") + private String ipaddr; + + /** 登录地点 */ + @Excel(name = "登录地点") + private String loginLocation; + + /** 浏览器类型 */ + @Excel(name = "浏览器") + private String browser; + + /** 操作系统 */ + @Excel(name = "操作系统") + private String os; + + /** 提示消息 */ + @Excel(name = "提示消息") + private String msg; + /** + * 多租户编号 + */ + private Long tenantId; + /** 访问时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "访问时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") + private Date loginTime; + + public Long getInfoId() + { + return infoId; + } + + public void setInfoId(Long infoId) + { + this.infoId = infoId; + } + + public String getUserName() + { + return userName; + } + + public void setUserName(String userName) + { + this.userName = userName; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getIpaddr() + { + return ipaddr; + } + + public void setIpaddr(String ipaddr) + { + this.ipaddr = ipaddr; + } + + public String getLoginLocation() + { + return loginLocation; + } + + public void setLoginLocation(String loginLocation) + { + this.loginLocation = loginLocation; + } + + public String getBrowser() + { + return browser; + } + + public void setBrowser(String browser) + { + this.browser = browser; + } + + public String getOs() + { + return os; + } + + public void setOs(String os) + { + this.os = os; + } + + public String getMsg() + { + return msg; + } + + public void setMsg(String msg) + { + this.msg = msg; + } + + public Date getLoginTime() + { + return loginTime; + } + + public void setLoginTime(Date loginTime) + { + this.loginTime = loginTime; + } + + public Long getTenantId() { + return this.tenantId; + } + + public void setTenantId(Long tenantId) { + this.tenantId = tenantId; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysNotice.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysNotice.java new file mode 100644 index 00000000..0007cb4a --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysNotice.java @@ -0,0 +1,106 @@ +package com.ruoyi.system.domain; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.core.domain.BaseEntity; +import com.ruoyi.common.xss.Xss; + +/** + * 通知公告表 sys_notice + * + * @author ruoyi + */ +public class SysNotice extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 公告ID */ + @TableId(type = IdType.AUTO) + private Long noticeId; + + /** 公告标题 */ + private String noticeTitle; + + /** 公告类型(1通知 2公告) */ + private String noticeType; + + /** 公告内容 */ + private String noticeContent; + + /** 公告状态(0正常 1关闭) */ + private String status; + + public Long getNoticeId() + { + return noticeId; + } + + public void setNoticeId(Long noticeId) + { + this.noticeId = noticeId; + } + + public void setNoticeTitle(String noticeTitle) + { + this.noticeTitle = noticeTitle; + } + + @Xss(message = "公告标题不能包含脚本字符") + @NotBlank(message = "公告标题不能为空") + @Size(min = 0, max = 50, message = "公告标题不能超过50个字符") + public String getNoticeTitle() + { + return noticeTitle; + } + + public void setNoticeType(String noticeType) + { + this.noticeType = noticeType; + } + + public String getNoticeType() + { + return noticeType; + } + + public void setNoticeContent(String noticeContent) + { + this.noticeContent = noticeContent; + } + + public String getNoticeContent() + { + return noticeContent; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getStatus() + { + return status; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("noticeId", getNoticeId()) + .append("noticeTitle", getNoticeTitle()) + .append("noticeType", getNoticeType()) + .append("noticeContent", getNoticeContent()) + .append("status", getStatus()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOperLog.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOperLog.java new file mode 100644 index 00000000..2a6f9d5b --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOperLog.java @@ -0,0 +1,259 @@ +package com.ruoyi.system.domain; + +import java.util.Date; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 操作日志记录表 oper_log + * + * @author ruoyi + */ +public class SysOperLog extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 日志主键 */ + @Excel(name = "操作序号", cellType = ColumnType.NUMERIC) + @TableId(type = IdType.AUTO) + private Long operId; + + /** 操作模块 */ + @Excel(name = "操作模块") + private String title; + + /** 业务类型(0其它 1新增 2修改 3删除) */ + @Excel(name = "业务类型", readConverterExp = "0=其它,1=新增,2=修改,3=删除,4=授权,5=导出,6=导入,7=强退,8=生成代码,9=清空数据") + private Integer businessType; + + /** 业务类型数组 */ + private Integer[] businessTypes; + + /** 请求方法 */ + @Excel(name = "请求方法") + private String method; + + /** 请求方式 */ + @Excel(name = "请求方式") + private String requestMethod; + + /** 操作类别(0其它 1后台用户 2手机端用户) */ + @Excel(name = "操作类别", readConverterExp = "0=其它,1=后台用户,2=手机端用户") + private Integer operatorType; + + /** 操作人员 */ + @Excel(name = "操作人员") + private String operName; + + /** 部门名称 */ + @Excel(name = "部门名称") + private String deptName; + + /** 请求url */ + @Excel(name = "请求地址") + private String operUrl; + + /** 操作地址 */ + @Excel(name = "操作地址") + private String operIp; + + /** 操作地点 */ + @Excel(name = "操作地点") + private String operLocation; + + /** 请求参数 */ + @Excel(name = "请求参数") + private String operParam; + + /** 返回参数 */ + @Excel(name = "返回参数") + private String jsonResult; + + /** 操作状态(0正常 1异常) */ + @Excel(name = "状态", readConverterExp = "0=正常,1=异常") + private Integer status; + + /** 错误消息 */ + @Excel(name = "错误消息") + private String errorMsg; + + /** 操作时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "操作时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") + private Date operTime; + + public Long getOperId() + { + return operId; + } + + public void setOperId(Long operId) + { + this.operId = operId; + } + + public String getTitle() + { + return title; + } + + public void setTitle(String title) + { + this.title = title; + } + + public Integer getBusinessType() + { + return businessType; + } + + public void setBusinessType(Integer businessType) + { + this.businessType = businessType; + } + + public Integer[] getBusinessTypes() + { + return businessTypes; + } + + public void setBusinessTypes(Integer[] businessTypes) + { + this.businessTypes = businessTypes; + } + + public String getMethod() + { + return method; + } + + public void setMethod(String method) + { + this.method = method; + } + + public String getRequestMethod() + { + return requestMethod; + } + + public void setRequestMethod(String requestMethod) + { + this.requestMethod = requestMethod; + } + + public Integer getOperatorType() + { + return operatorType; + } + + public void setOperatorType(Integer operatorType) + { + this.operatorType = operatorType; + } + + public String getOperName() + { + return operName; + } + + public void setOperName(String operName) + { + this.operName = operName; + } + + public String getDeptName() + { + return deptName; + } + + public void setDeptName(String deptName) + { + this.deptName = deptName; + } + + public String getOperUrl() + { + return operUrl; + } + + public void setOperUrl(String operUrl) + { + this.operUrl = operUrl; + } + + public String getOperIp() + { + return operIp; + } + + public void setOperIp(String operIp) + { + this.operIp = operIp; + } + + public String getOperLocation() + { + return operLocation; + } + + public void setOperLocation(String operLocation) + { + this.operLocation = operLocation; + } + + public String getOperParam() + { + return operParam; + } + + public void setOperParam(String operParam) + { + this.operParam = operParam; + } + + public String getJsonResult() + { + return jsonResult; + } + + public void setJsonResult(String jsonResult) + { + this.jsonResult = jsonResult; + } + + public Integer getStatus() + { + return status; + } + + public void setStatus(Integer status) + { + this.status = status; + } + + public String getErrorMsg() + { + return errorMsg; + } + + public void setErrorMsg(String errorMsg) + { + this.errorMsg = errorMsg; + } + + public Date getOperTime() + { + return operTime; + } + + public void setOperTime(Date operTime) + { + this.operTime = operTime; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysPost.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysPost.java new file mode 100644 index 00000000..52d49c85 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysPost.java @@ -0,0 +1,129 @@ +package com.ruoyi.system.domain; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 岗位表 sys_post + * + * @author ruoyi + */ +public class SysPost extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 岗位序号 */ + @Excel(name = "岗位序号", cellType = ColumnType.NUMERIC) + @TableId(type = IdType.AUTO) + private Long postId; + + /** 岗位编码 */ + @Excel(name = "岗位编码") + private String postCode; + + /** 岗位名称 */ + @Excel(name = "岗位名称") + private String postName; + + /** 岗位排序 */ + @Excel(name = "岗位排序") + private String postSort; + + /** 状态(0正常 1停用) */ + @Excel(name = "状态", readConverterExp = "0=正常,1=停用") + private String status; + + /** 用户是否存在此岗位标识 默认不存在 */ + @TableField(exist = false) + private boolean flag = false; + + public Long getPostId() + { + return postId; + } + + public void setPostId(Long postId) + { + this.postId = postId; + } + + @NotBlank(message = "岗位编码不能为空") + @Size(min = 0, max = 64, message = "岗位编码长度不能超过64个字符") + public String getPostCode() + { + return postCode; + } + + public void setPostCode(String postCode) + { + this.postCode = postCode; + } + + @NotBlank(message = "岗位名称不能为空") + @Size(min = 0, max = 50, message = "岗位名称长度不能超过50个字符") + public String getPostName() + { + return postName; + } + + public void setPostName(String postName) + { + this.postName = postName; + } + + @NotBlank(message = "显示顺序不能为空") + public String getPostSort() + { + return postSort; + } + + public void setPostSort(String postSort) + { + this.postSort = postSort; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public boolean isFlag() + { + return flag; + } + + public void setFlag(boolean flag) + { + this.flag = flag; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("postId", getPostId()) + .append("postCode", getPostCode()) + .append("postName", getPostName()) + .append("postSort", getPostSort()) + .append("status", getStatus()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleDept.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleDept.java new file mode 100644 index 00000000..47b21bf7 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleDept.java @@ -0,0 +1,46 @@ +package com.ruoyi.system.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 角色和部门关联 sys_role_dept + * + * @author ruoyi + */ +public class SysRoleDept +{ + /** 角色ID */ + private Long roleId; + + /** 部门ID */ + private Long deptId; + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + public Long getDeptId() + { + return deptId; + } + + public void setDeptId(Long deptId) + { + this.deptId = deptId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("roleId", getRoleId()) + .append("deptId", getDeptId()) + .toString(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleMenu.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleMenu.java new file mode 100644 index 00000000..de10a747 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleMenu.java @@ -0,0 +1,46 @@ +package com.ruoyi.system.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 角色和菜单关联 sys_role_menu + * + * @author ruoyi + */ +public class SysRoleMenu +{ + /** 角色ID */ + private Long roleId; + + /** 菜单ID */ + private Long menuId; + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + public Long getMenuId() + { + return menuId; + } + + public void setMenuId(Long menuId) + { + this.menuId = menuId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("roleId", getRoleId()) + .append("menuId", getMenuId()) + .toString(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysTreeDict.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysTreeDict.java new file mode 100644 index 00000000..011f05ce --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysTreeDict.java @@ -0,0 +1,88 @@ +package com.ruoyi.system.domain; + +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.Data; + +import java.util.Date; + +@Data +public class SysTreeDict { + + private static final long serialVersionUID = 1L; + /** + * 主键 + */ + @Excel(name = "主键") + @TableId(value = "ID", type = IdType.ASSIGN_ID) + private String id; + + /** + * 重写:分类名称 + */ + @Excel(name = "分类名称") + private String name; + + /** + * 编码 + */ + @Excel(name = "编码") + private String code; + + /** + * 类别(0:树型结构;1:平铺结构) + */ + @Excel(name = "类别") + private String struType; + + /** + * 备注 + */ + private String remark; + + /** + * 租户ID + */ + @Excel(name = "租户ID") + private String tenantId; + + /** + * 创建者 + */ + @TableField(fill = FieldFill.INSERT) + private String createBy; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** + * 更新者 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + /** + * 删除标志 + */ + @TableLogic + private String delFlag; + + /** + * 是否系统参数 + */ + @Excel(name = "图标地址") + private String isSysParam; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysTreeDictData.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysTreeDictData.java new file mode 100644 index 00000000..5d6b0d33 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysTreeDictData.java @@ -0,0 +1,37 @@ +package com.ruoyi.system.domain; + +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.DictTreeEntity; +import lombok.Data; + +import java.io.Serializable; + +/** + * @author wangzongrun + */ +@Data +public class SysTreeDictData extends DictTreeEntity implements Serializable { + + private static final long serialVersionUID = 1L; + + + /** + * 树形类别 + */ + @Excel(name = "树形类别") + private String treeDict; + + /** + * 路径 + */ + @Excel(name = "路径") + private String path; + + /** + * 图标地址 + */ + @Excel(name = "图标地址") + private String icon; + + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserOnline.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserOnline.java new file mode 100644 index 00000000..2bbd3188 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserOnline.java @@ -0,0 +1,113 @@ +package com.ruoyi.system.domain; + +/** + * 当前在线会话 + * + * @author ruoyi + */ +public class SysUserOnline +{ + /** 会话编号 */ + private String tokenId; + + /** 部门名称 */ + private String deptName; + + /** 用户名称 */ + private String userName; + + /** 登录IP地址 */ + private String ipaddr; + + /** 登录地址 */ + private String loginLocation; + + /** 浏览器类型 */ + private String browser; + + /** 操作系统 */ + private String os; + + /** 登录时间 */ + private Long loginTime; + + public String getTokenId() + { + return tokenId; + } + + public void setTokenId(String tokenId) + { + this.tokenId = tokenId; + } + + public String getDeptName() + { + return deptName; + } + + public void setDeptName(String deptName) + { + this.deptName = deptName; + } + + public String getUserName() + { + return userName; + } + + public void setUserName(String userName) + { + this.userName = userName; + } + + public String getIpaddr() + { + return ipaddr; + } + + public void setIpaddr(String ipaddr) + { + this.ipaddr = ipaddr; + } + + public String getLoginLocation() + { + return loginLocation; + } + + public void setLoginLocation(String loginLocation) + { + this.loginLocation = loginLocation; + } + + public String getBrowser() + { + return browser; + } + + public void setBrowser(String browser) + { + this.browser = browser; + } + + public String getOs() + { + return os; + } + + public void setOs(String os) + { + this.os = os; + } + + public Long getLoginTime() + { + return loginTime; + } + + public void setLoginTime(Long loginTime) + { + this.loginTime = loginTime; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserPost.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserPost.java new file mode 100644 index 00000000..56662dd0 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserPost.java @@ -0,0 +1,46 @@ +package com.ruoyi.system.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 用户和岗位关联 sys_user_post + * + * @author ruoyi + */ +public class SysUserPost +{ + /** 用户ID */ + private Long userId; + + /** 岗位ID */ + private Long postId; + + public Long getUserId() + { + return userId; + } + + public void setUserId(Long userId) + { + this.userId = userId; + } + + public Long getPostId() + { + return postId; + } + + public void setPostId(Long postId) + { + this.postId = postId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("userId", getUserId()) + .append("postId", getPostId()) + .toString(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserRole.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserRole.java new file mode 100644 index 00000000..4d158101 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserRole.java @@ -0,0 +1,46 @@ +package com.ruoyi.system.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 用户和角色关联 sys_user_role + * + * @author ruoyi + */ +public class SysUserRole +{ + /** 用户ID */ + private Long userId; + + /** 角色ID */ + private Long roleId; + + public Long getUserId() + { + return userId; + } + + public void setUserId(Long userId) + { + this.userId = userId; + } + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("userId", getUserId()) + .append("roleId", getRoleId()) + .toString(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/TenantDO.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/TenantDO.java new file mode 100644 index 00000000..6a973427 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/TenantDO.java @@ -0,0 +1,84 @@ +package com.ruoyi.system.domain; + + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.ruoyi.common.mybatis.dataobject.BaseDO; +import lombok.*; + +import java.util.Date; + +/** + * 租户 DO + * + * @author 芋道源码 + */ +@TableName(value = "system_tenant", autoResultMap = true) +//@KeySequence("system_tenant_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class TenantDO extends BaseDO { + + /** + * 套餐编号 - 系统 + */ + public static final Long PACKAGE_ID_SYSTEM = 0L; + + /** + * 租户编号,自增 + */ + @TableId(type = IdType.AUTO) + private Long id; + /** + * 租户名,唯一 + */ + private String name; + /** + * 联系人的用户编号 + * + * 关联 {@link AdminUserDO#getId()} + */ + private Long contactUserId; + /** + * 联系人 + */ + private String contactName; + /** + * 联系手机 + */ + private String contactMobile; + /** + * 租户状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + /** + * 绑定域名 + * + * TODO 芋艿:目前是预留字段,未来会支持根据域名,自动查询到对应的租户。等等 + */ + private String domain; + /** + * 租户套餐编号 + * + * 关联 {@link TenantPackageDO#getId()} + * 特殊逻辑:系统内置租户,不使用套餐,暂时使用 {@link #PACKAGE_ID_SYSTEM} 标识 + */ + private Long packageId; + /** + * 过期时间 + */ + private Date expireTime; + /** + * 账号数量 + */ + private Integer accountCount; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/TenantPackageDO.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/TenantPackageDO.java new file mode 100644 index 00000000..6ef510d5 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/TenantPackageDO.java @@ -0,0 +1,50 @@ +package com.ruoyi.system.domain; + +import com.baomidou.mybatisplus.annotation.*; +import com.ruoyi.common.handler.JsonLongSetTypeHandler; +import com.ruoyi.common.mybatis.dataobject.BaseDO; +import lombok.*; + +import java.util.Set; + +/** + * 租户套餐 DO + * + * @author 芋道源码 + */ +@TableName(value = "system_tenant_package", autoResultMap = true) +//@KeySequence("system_tenant_package_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class TenantPackageDO extends BaseDO { + + /** + * 套餐编号,自增 + */ + @TableId(type = IdType.AUTO) + private Long id; + /** + * 套餐名,唯一 + */ + private String name; + /** + * 租户套餐状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + /** + * 备注 + */ + private String remark; + /** + * 关联的菜单编号 + */ + @TableField(typeHandler = JsonLongSetTypeHandler.class) + private Set menuIds; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/ImageVo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/ImageVo.java new file mode 100644 index 00000000..24cca81a --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/ImageVo.java @@ -0,0 +1,18 @@ +package com.ruoyi.system.domain.vo; + +import com.ruoyi.common.utils.StringUtils; +import lombok.Data; + +import java.io.Serializable; + +/** + * 路由显示信息 + * + * @author ruoyi + */ +@Data +public class ImageVo implements Serializable { + private static final long serialVersionUID=1L; + String name; + String url; +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/MetaVo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/MetaVo.java new file mode 100644 index 00000000..a5d5fdcc --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/MetaVo.java @@ -0,0 +1,106 @@ +package com.ruoyi.system.domain.vo; + +import com.ruoyi.common.utils.StringUtils; + +/** + * 路由显示信息 + * + * @author ruoyi + */ +public class MetaVo +{ + /** + * 设置该路由在侧边栏和面包屑中展示的名字 + */ + private String title; + + /** + * 设置该路由的图标,对应路径src/assets/icons/svg + */ + private String icon; + + /** + * 设置为true,则不会被 缓存 + */ + private boolean noCache; + + /** + * 内链地址(http(s)://开头) + */ + private String link; + + public MetaVo() + { + } + + public MetaVo(String title, String icon) + { + this.title = title; + this.icon = icon; + } + + public MetaVo(String title, String icon, boolean noCache) + { + this.title = title; + this.icon = icon; + this.noCache = noCache; + } + + public MetaVo(String title, String icon, String link) + { + this.title = title; + this.icon = icon; + this.link = link; + } + + public MetaVo(String title, String icon, boolean noCache, String link) + { + this.title = title; + this.icon = icon; + this.noCache = noCache; + if (StringUtils.ishttp(link)) + { + this.link = link; + } + } + + public boolean isNoCache() + { + return noCache; + } + + public void setNoCache(boolean noCache) + { + this.noCache = noCache; + } + + public String getTitle() + { + return title; + } + + public void setTitle(String title) + { + this.title = title; + } + + public String getIcon() + { + return icon; + } + + public void setIcon(String icon) + { + this.icon = icon; + } + + public String getLink() + { + return link; + } + + public void setLink(String link) + { + this.link = link; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/NameIdVo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/NameIdVo.java new file mode 100644 index 00000000..f990614d --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/NameIdVo.java @@ -0,0 +1,17 @@ +package com.ruoyi.system.domain.vo; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 路由显示信息 + * + * @author ruoyi + */ +@Data +public class NameIdVo implements Serializable { + private static final long serialVersionUID=1L; + String name; + Long id; +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/RouterVo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/RouterVo.java new file mode 100644 index 00000000..afff8c9c --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/RouterVo.java @@ -0,0 +1,148 @@ +package com.ruoyi.system.domain.vo; + +import com.fasterxml.jackson.annotation.JsonInclude; +import java.util.List; + +/** + * 路由配置信息 + * + * @author ruoyi + */ +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class RouterVo +{ + /** + * 路由名字 + */ + private String name; + + /** + * 路由地址 + */ + private String path; + + /** + * 是否隐藏路由,当设置 true 的时候该路由不会再侧边栏出现 + */ + private boolean hidden; + + /** + * 重定向地址,当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 + */ + private String redirect; + + /** + * 组件地址 + */ + private String component; + + /** + * 路由参数:如 {"id": 1, "name": "ry"} + */ + private String query; + + /** + * 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面 + */ + private Boolean alwaysShow; + + /** + * 其他元素 + */ + private MetaVo meta; + + /** + * 子路由 + */ + private List children; + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public String getPath() + { + return path; + } + + public void setPath(String path) + { + this.path = path; + } + + public boolean getHidden() + { + return hidden; + } + + public void setHidden(boolean hidden) + { + this.hidden = hidden; + } + + public String getRedirect() + { + return redirect; + } + + public void setRedirect(String redirect) + { + this.redirect = redirect; + } + + public String getComponent() + { + return component; + } + + public void setComponent(String component) + { + this.component = component; + } + + public String getQuery() + { + return query; + } + + public void setQuery(String query) + { + this.query = query; + } + + public Boolean getAlwaysShow() + { + return alwaysShow; + } + + public void setAlwaysShow(Boolean alwaysShow) + { + this.alwaysShow = alwaysShow; + } + + public MetaVo getMeta() + { + return meta; + } + + public void setMeta(MetaVo meta) + { + this.meta = meta; + } + + public List getChildren() + { + return children; + } + + public void setChildren(List children) + { + this.children = children; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/UserInfoVo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/UserInfoVo.java new file mode 100644 index 00000000..af8b828d --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/UserInfoVo.java @@ -0,0 +1,18 @@ +package com.ruoyi.system.domain.vo; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 路由显示信息 + * + * @author ruoyi + */ +@Data +public class UserInfoVo implements Serializable { + private static final long serialVersionUID=1L; + private Long userId; + private String userName; + private String nickName; +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/menu/MenuSimpleRespVO.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/menu/MenuSimpleRespVO.java new file mode 100644 index 00000000..a685110e --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/menu/MenuSimpleRespVO.java @@ -0,0 +1,27 @@ +package com.ruoyi.system.domain.vo.menu; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@ApiModel("管理后台 - 菜单精简信息 Response VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class MenuSimpleRespVO { + + @ApiModelProperty(value = "菜单编号", required = true, example = "1024") + private Long id; + + @ApiModelProperty(value = "菜单名称", required = true, example = "芋道") + private String name; + + @ApiModelProperty(value = "父菜单 ID", required = true, example = "1024") + private Long parentId; + + @ApiModelProperty(value = "类型", required = true, example = "1", notes = "参见 MenuTypeEnum 枚举类") + private Integer type; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/packages/TenantPackageBaseVO.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/packages/TenantPackageBaseVO.java new file mode 100644 index 00000000..f6fec063 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/packages/TenantPackageBaseVO.java @@ -0,0 +1,31 @@ +package com.ruoyi.system.domain.vo.packages; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotNull; +import java.util.Set; + +/** +* 租户套餐 Base VO,提供给添加、修改、详细的子 VO 使用 +* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 +*/ +@Data +public class TenantPackageBaseVO { + + @ApiModelProperty(value = "套餐名", required = true, example = "VIP") + @NotNull(message = "套餐名不能为空") + private String name; + + @ApiModelProperty(value = "状态", required = true, example = "1", notes = "参见 CommonStatusEnum 枚举") + @NotNull(message = "状态不能为空") + private Integer status; + + @ApiModelProperty(value = "备注", example = "好") + private String remark; + + @ApiModelProperty(value = "关联的菜单编号", required = true) + @NotNull(message = "关联的菜单编号不能为空") + private Set menuIds; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/packages/TenantPackageCreateReqVO.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/packages/TenantPackageCreateReqVO.java new file mode 100644 index 00000000..2a9079b6 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/packages/TenantPackageCreateReqVO.java @@ -0,0 +1,14 @@ +package com.ruoyi.system.domain.vo.packages; + +import io.swagger.annotations.ApiModel; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@ApiModel("管理后台 - 租户套餐创建 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class TenantPackageCreateReqVO extends TenantPackageBaseVO { + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/packages/TenantPackagePageReqVO.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/packages/TenantPackagePageReqVO.java new file mode 100644 index 00000000..d3e2613e --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/packages/TenantPackagePageReqVO.java @@ -0,0 +1,32 @@ +package com.ruoyi.system.domain.vo.packages; + +import com.ruoyi.common.mybatis.pojo.PageParam; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + + +@ApiModel("管理后台 - 租户套餐分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class TenantPackagePageReqVO extends PageParam { + + @ApiModelProperty(value = "套餐名", example = "VIP") + private String name; + + @ApiModelProperty(value = "状态", example = "1") + private Integer status; + + @ApiModelProperty(value = "备注", example = "好") + private String remark; + + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "创建时间") + private Date[] createTime; +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/packages/TenantPackageRespVO.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/packages/TenantPackageRespVO.java new file mode 100644 index 00000000..8e557c44 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/packages/TenantPackageRespVO.java @@ -0,0 +1,23 @@ +package com.ruoyi.system.domain.vo.packages; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.Date; + +@ApiModel("管理后台 - 租户套餐 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class TenantPackageRespVO extends TenantPackageBaseVO { + + @ApiModelProperty(value = "套餐编号", required = true, example = "1024") + private Long id; + + @ApiModelProperty(value = "创建时间", required = true) + private Date createTime; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/packages/TenantPackageSimpleRespVO.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/packages/TenantPackageSimpleRespVO.java new file mode 100644 index 00000000..1aca32c4 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/packages/TenantPackageSimpleRespVO.java @@ -0,0 +1,21 @@ +package com.ruoyi.system.domain.vo.packages; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +@ApiModel("管理后台 - 租户套餐精简 Response VO") +@Data +public class TenantPackageSimpleRespVO { + + @ApiModelProperty(value = "套餐编号", required = true, example = "1024") + @NotNull(message = "套餐编号不能为空") + private Long id; + + @ApiModelProperty(value = "套餐名", required = true, example = "VIP") + @NotNull(message = "套餐名不能为空") + private String name; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/packages/TenantPackageUpdateReqVO.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/packages/TenantPackageUpdateReqVO.java new file mode 100644 index 00000000..eea2ff17 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/packages/TenantPackageUpdateReqVO.java @@ -0,0 +1,21 @@ +package com.ruoyi.system.domain.vo.packages; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import javax.validation.constraints.NotNull; + +@ApiModel("管理后台 - 租户套餐更新 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class TenantPackageUpdateReqVO extends TenantPackageBaseVO { + + @ApiModelProperty(value = "套餐编号", required = true, example = "1024") + @NotNull(message = "套餐编号不能为空") + private Long id; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/report/ReportDataRequestVo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/report/ReportDataRequestVo.java new file mode 100644 index 00000000..7512585a --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/report/ReportDataRequestVo.java @@ -0,0 +1,16 @@ +package com.ruoyi.system.domain.vo.report; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.OptBoolean; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +@Data +public class ReportDataRequestVo implements Serializable { + + String reportType; + private Date reportTime; + private String dateType ="0"; //0按日,1按月 +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/report/ReportDataResponseVo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/report/ReportDataResponseVo.java new file mode 100644 index 00000000..aacde204 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/report/ReportDataResponseVo.java @@ -0,0 +1,19 @@ +package com.ruoyi.system.domain.vo.report; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +@Data +public class ReportDataResponseVo implements Serializable { + + Long id; + String name; + /** 创建时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "日期",sort = 10,dateFormat="yyyy-MM-dd") + private Date createTime; +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/report/ReportDownLoadVo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/report/ReportDownLoadVo.java new file mode 100644 index 00000000..53a39585 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/report/ReportDownLoadVo.java @@ -0,0 +1,18 @@ +package com.ruoyi.system.domain.vo.report; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.OptBoolean; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +@Data +public class ReportDownLoadVo implements Serializable { + + String reportType; + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-M-d", lenient = OptBoolean.TRUE) + private Date reportTime; + private String dateType = "0"; //0按日,1按月 + private Long id; +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/task/BatchSettingResqVo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/task/BatchSettingResqVo.java new file mode 100644 index 00000000..985b6ff8 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/task/BatchSettingResqVo.java @@ -0,0 +1,63 @@ +package com.ruoyi.system.domain.vo.task; + + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.Date; +import java.util.Set; + +/** + * 批量设置 + */ +@Data +public class BatchSettingResqVo { + /** + * 任务ID + */ + private Long taskId; + + /** + * 管理人 + */ + @ApiModelProperty(value = "管理人") + private String manager; + + /** + * 周期 + */ + @ApiModelProperty(value = "周期") + private String cycle; + + /** + * 提单人员 + */ + @ApiModelProperty(value = "提单人员") + private Set submitUserInfo; + + /** + * 派单人员 + */ + @ApiModelProperty(value = "派单人员") + private Set deliveryUserInfo; + + /** + * 审核人员 + */ + @ApiModelProperty(value = "审核人员") + private Set auditUserInfo; + + /** + * 抄送人员 + */ + @ApiModelProperty(value = "抄送人员") + private Set sendUserInfo; + + /** 任务开始时间 */ + @ApiModelProperty(value = "任务开始时间") + private Date startTime; + + /** 任务结束时间 */ + @ApiModelProperty(value = "任务结束时间") + private Date endTime; +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/task/PointResqVo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/task/PointResqVo.java new file mode 100644 index 00000000..60e079d1 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/task/PointResqVo.java @@ -0,0 +1,68 @@ +package com.ruoyi.system.domain.vo.task; + + +import com.ruoyi.system.domain.vo.ImageVo; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; +import java.util.Set; + +/** + * 点位巡查新增/编辑Vo + */ +@Data +public class PointResqVo { + /** + * 任务ID + */ + private Long taskId; + /** 点位编码 */ + @ApiModelProperty(value = "点位编码") + private String taskNo; + /** + * 点位名称 + */ + private String taskName; + + /** + * 位置 + */ + @ApiModelProperty(value = "位置") + private String position; + + /** + * 管理人 + */ + @ApiModelProperty(value = "管理人") + private String manager; + + /** + * 周期 + */ + @ApiModelProperty(value = "周期") + private String cycle; + + /** + * 巡查设备 + */ + @ApiModelProperty(value = "巡查设备") + private Set deviceInfo; + + /** + * 备注 + */ + private String remark; + + /** + * 巡检要求 + */ + @ApiModelProperty(value = "巡检要求") + private String requireDesc; + + /** + * 点位实拍 + */ + @ApiModelProperty(value = "点位实拍") + private List taskImg; +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/task/SettingUserInfoResqVo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/task/SettingUserInfoResqVo.java new file mode 100644 index 00000000..14d69f1c --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/task/SettingUserInfoResqVo.java @@ -0,0 +1,51 @@ +package com.ruoyi.system.domain.vo.task; + + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.Date; +import java.util.Set; + +/** + * 任务设置人员信息、开始结束时间的请求参数 + */ +@Data +public class SettingUserInfoResqVo { + /** + * 任务ID + */ + private Long taskId; + + /** + * 提单人员 + */ + @ApiModelProperty(value = "提单人员") + private Set submitUserInfo; + + /** + * 派单人员 + */ + @ApiModelProperty(value = "派单人员") + private Set deliveryUserInfo; + + /** + * 审核人员 + */ + @ApiModelProperty(value = "审核人员") + private Set auditUserInfo; + + /** + * 抄送人员 + */ + @ApiModelProperty(value = "抄送人员") + private Set sendUserInfo; + + /** 任务开始时间 */ + @ApiModelProperty(value = "任务开始时间") + private Date startTime; + + /** 任务结束时间 */ + @ApiModelProperty(value = "任务结束时间") + private Date endTime; +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/tenant/TenantBaseVO.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/tenant/TenantBaseVO.java new file mode 100644 index 00000000..4cd80920 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/tenant/TenantBaseVO.java @@ -0,0 +1,48 @@ +package com.ruoyi.system.domain.vo.tenant; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.hibernate.validator.constraints.URL; + +import javax.validation.constraints.NotNull; +import java.util.Date; + +/** +* 租户 Base VO,提供给添加、修改、详细的子 VO 使用 +* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 +*/ +@Data +public class TenantBaseVO { + + @ApiModelProperty(value = "租户名", required = true, example = "芋道") + @NotNull(message = "租户名不能为空") + private String name; + + @ApiModelProperty(value = "联系人", required = true, example = "芋艿") + @NotNull(message = "联系人不能为空") + private String contactName; + + @ApiModelProperty(value = "联系手机", example = "15601691300") + private String contactMobile; + + @ApiModelProperty(value = "租户状态", required = true, example = "1") + @NotNull(message = "租户状态") + private Integer status; + + @ApiModelProperty(value = "绑定域名", example = "https://www.iocoder.cn") + @URL(message = "绑定域名的地址非 URL 格式") + private String domain; + + @ApiModelProperty(value = "租户套餐编号", required = true, example = "1024") + @NotNull(message = "租户套餐编号不能为空") + private Long packageId; + + @ApiModelProperty(value = "过期时间", required = true) + @NotNull(message = "过期时间不能为空") + private Date expireTime; + + @ApiModelProperty(value = "账号数量", required = true, example = "1024") + @NotNull(message = "账号数量不能为空") + private Integer accountCount; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/tenant/TenantCreateReqVO.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/tenant/TenantCreateReqVO.java new file mode 100644 index 00000000..8830d8c9 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/tenant/TenantCreateReqVO.java @@ -0,0 +1,32 @@ +package com.ruoyi.system.domain.vo.tenant; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; + +@ApiModel("管理后台 - 租户创建 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class TenantCreateReqVO extends TenantBaseVO { + + @ApiModelProperty(value = "用户账号", required = true, example = "yudao") + @NotBlank(message = "用户账号不能为空") + @Pattern(regexp = "^[a-zA-Z0-9]{4,30}$", message = "用户账号由 数字、字母 组成") + @Size(min = 4, max = 30, message = "用户账号长度为 4-30 个字符") + private String username; + + @ApiModelProperty(value = "密码", required = true, example = "123456") + @NotEmpty(message = "密码不能为空") + @Length(min = 4, max = 16, message = "密码长度为 4-16 位") + private String password; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/tenant/TenantExcelVO.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/tenant/TenantExcelVO.java new file mode 100644 index 00000000..e36ade18 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/tenant/TenantExcelVO.java @@ -0,0 +1,38 @@ +package com.ruoyi.system.domain.vo.tenant; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.ruoyi.common.annotation.DictFormat; +import com.ruoyi.common.convert.DictConvert; +import lombok.Data; + +import java.util.Date; + + +/** + * 租户 Excel VO + * + * @author 芋道源码 + */ +@Data +public class TenantExcelVO { + + @ExcelProperty("租户编号") + private Long id; + + @ExcelProperty("租户名") + private String name; + + @ExcelProperty("联系人") + private String contactName; + + @ExcelProperty("联系手机") + private String contactMobile; + + @ExcelProperty(value = "状态", converter = DictConvert.class) + @DictFormat("sys_common_status") + private Integer status; + + @ExcelProperty("创建时间") + private Date createTime; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/tenant/TenantExportReqVO.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/tenant/TenantExportReqVO.java new file mode 100644 index 00000000..a1340cdd --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/tenant/TenantExportReqVO.java @@ -0,0 +1,31 @@ +package com.ruoyi.system.domain.vo.tenant; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + + +@ApiModel(value = "管理后台 - 租户 Excel 导出 Request VO", description = "参数和 TenantPageReqVO 是一致的") +@Data +public class TenantExportReqVO { + + @ApiModelProperty(value = "租户名", example = "芋道") + private String name; + + @ApiModelProperty(value = "联系人", example = "芋艿") + private String contactName; + + @ApiModelProperty(value = "联系手机", example = "15601691300") + private String contactMobile; + + @ApiModelProperty(value = "租户状态(0正常 1停用)", example = "1") + private Integer status; + + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "创建时间") + private Date[] createTime; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/tenant/TenantPageReqVO.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/tenant/TenantPageReqVO.java new file mode 100644 index 00000000..6d800158 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/tenant/TenantPageReqVO.java @@ -0,0 +1,36 @@ +package com.ruoyi.system.domain.vo.tenant; + +import com.ruoyi.common.mybatis.pojo.PageParam; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + + +@ApiModel("管理后台 - 租户分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class TenantPageReqVO extends PageParam { + + @ApiModelProperty(value = "租户名", example = "芋道") + private String name; + + @ApiModelProperty(value = "联系人", example = "芋艿") + private String contactName; + + @ApiModelProperty(value = "联系手机", example = "15601691300") + private String contactMobile; + + @ApiModelProperty(value = "租户状态(0正常 1停用)", example = "1") + private Integer status; + + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "创建时间") + private Date[] createTime; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/tenant/TenantRespVO.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/tenant/TenantRespVO.java new file mode 100644 index 00000000..0e39a9fa --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/tenant/TenantRespVO.java @@ -0,0 +1,23 @@ +package com.ruoyi.system.domain.vo.tenant; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.Date; + +@ApiModel("管理后台 - 租户 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class TenantRespVO extends TenantBaseVO { + + @ApiModelProperty(value = "租户编号", required = true, example = "1024") + private Long id; + + @ApiModelProperty(value = "创建时间", required = true) + private Date createTime; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/tenant/TenantUpdateReqVO.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/tenant/TenantUpdateReqVO.java new file mode 100644 index 00000000..622ee29c --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/tenant/TenantUpdateReqVO.java @@ -0,0 +1,21 @@ +package com.ruoyi.system.domain.vo.tenant; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import javax.validation.constraints.NotNull; + +@ApiModel("管理后台 - 租户更新 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class TenantUpdateReqVO extends TenantBaseVO { + + @ApiModelProperty(value = "租户编号", required = true, example = "1024") + @NotNull(message = "租户编号不能为空") + private Long id; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/user/UserSimpleRespVO.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/user/UserSimpleRespVO.java new file mode 100644 index 00000000..b14d2f7b --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/user/UserSimpleRespVO.java @@ -0,0 +1,23 @@ +package com.ruoyi.system.domain.vo.user; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Schema(description = "管理后台 - 用户精简信息 Response VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class UserSimpleRespVO { + + @Schema(description = "用户编号", required = true, example = "1024") + private Long userId; + + @Schema(description = "用户昵称", required = true, example = "芋道") + private String nickName; + + /** 用户账号 */ + private String userName; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/enums/ReportTypeEnum.java b/ruoyi-system/src/main/java/com/ruoyi/system/enums/ReportTypeEnum.java new file mode 100644 index 00000000..ac5eaa05 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/enums/ReportTypeEnum.java @@ -0,0 +1,64 @@ +package com.ruoyi.system.enums; + +import cn.hutool.core.util.StrUtil; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.system.service.ISysDictTypeService; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * + */ +@Getter +@AllArgsConstructor +public enum ReportTypeEnum { + + KEYAREAS("情况登记表", ISysConfigService.class, ISysDictTypeService.class,"area_id","create_time"), + ; + + + /** + * 表单名称,注意也是找模板文件的使用名称 + */ + private final String title; + /** + * service class + */ + private final Class serviceClass; + private final Class entityClass; + /** + * 数据库中的ID字段名 + */ + private final String idField; + /** + * 数据库中的时间统计字段名 + */ + private final String timeField; + /** + * 根据名称查找枚举值 + * @param name 枚举名称 + * @return 枚举值,如果找不到返回 null + */ + public static ReportTypeEnum getEnumByName(String name) { + for (ReportTypeEnum value : values()) { + if (value.name().equals(name)) { + return value; + } + } + return null; + } + + /** + * 实体中的ID字段名 + */ + public String getIdFieldToCamerlCase(){ + return StrUtil.toCamelCase(this.getIdField()); + } + /** + * 实体中的timeField字段名 + */ + public String getTimeFieldToCamerlCase(){ + return StrUtil.toCamelCase(this.getTimeField()); + } + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysConfigMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysConfigMapper.java new file mode 100644 index 00000000..f6d161ff --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysConfigMapper.java @@ -0,0 +1,70 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Mapper; +import com.ruoyi.system.domain.SysConfig; + +/** + * 参数配置 数据层 + * + * @author ruoyi +*/ +@Mapper +public interface SysConfigMapper +{ + /** + * 查询参数配置信息 + * + * @param config 参数配置信息 + * @return 参数配置信息 + */ + public SysConfig selectConfig(SysConfig config); + + /** + * 查询参数配置列表 + * + * @param config 参数配置信息 + * @return 参数配置集合 + */ + public List selectConfigList(SysConfig config); + + /** + * 根据键名查询参数配置信息 + * + * @param configKey 参数键名 + * @return 参数配置信息 + */ + public SysConfig checkConfigKeyUnique(String configKey); + + /** + * 新增参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + public int insertConfig(SysConfig config); + + /** + * 修改参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + public int updateConfig(SysConfig config); + + /** + * 删除参数配置 + * + * @param configId 参数ID + * @return 结果 + */ + public int deleteConfigById(Long configId); + + /** + * 批量删除参数信息 + * + * @param configIds 需要删除的参数ID + * @return 结果 + */ + public int deleteConfigByIds(Long[] configIds); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java new file mode 100644 index 00000000..e70e1f8e --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java @@ -0,0 +1,122 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Mapper; + +import com.ruoyi.common.mybatis.mapper.BaseMapperX; +import org.apache.ibatis.annotations.Param; +import com.ruoyi.common.core.domain.entity.SysDept; + +/** + * 部门管理 数据层 + * + * @author ruoyi +*/ +@Mapper +public interface SysDeptMapper extends BaseMapperX +{ + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * @return 部门信息集合 + */ + public List selectDeptList(SysDept dept); + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @param deptCheckStrictly 部门树选择项是否关联显示 + * @return 选中部门列表 + */ + public List selectDeptListByRoleId(@Param("roleId") Long roleId, @Param("deptCheckStrictly") boolean deptCheckStrictly); + + /** + * 根据部门ID查询信息 + * + * @param deptId 部门ID + * @return 部门信息 + */ + public SysDept selectDeptById(Long deptId); + + /** + * 根据ID查询所有子部门 + * + * @param deptId 部门ID + * @return 部门列表 + */ + public List selectChildrenDeptById(Long deptId); + + /** + * 根据ID查询所有子部门(正常状态) + * + * @param deptId 部门ID + * @return 子部门数 + */ + public int selectNormalChildrenDeptById(Long deptId); + + /** + * 是否存在子节点 + * + * @param deptId 部门ID + * @return 结果 + */ + public int hasChildByDeptId(Long deptId); + + /** + * 查询部门是否存在用户 + * + * @param deptId 部门ID + * @return 结果 + */ + public int checkDeptExistUser(Long deptId); + + /** + * 校验部门名称是否唯一 + * + * @param deptName 部门名称 + * @param parentId 父部门ID + * @return 结果 + */ + public SysDept checkDeptNameUnique(@Param("deptName") String deptName, @Param("parentId") Long parentId); + + /** + * 新增部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + public int insertDept(SysDept dept); + + /** + * 修改部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + public int updateDept(SysDept dept); + + /** + * 修改所在部门正常状态 + * + * @param deptIds 部门ID组 + */ + public void updateDeptStatusNormal(Long[] deptIds); + + /** + * 修改子元素关系 + * + * @param depts 子元素 + * @return 结果 + */ + public int updateDeptChildren(@Param("depts") List depts); + + /** + * 删除部门管理信息 + * + * @param deptId 部门ID + * @return 结果 + */ + public int deleteDeptById(Long deptId); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictDataMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictDataMapper.java new file mode 100644 index 00000000..67736272 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictDataMapper.java @@ -0,0 +1,99 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Mapper; + +import com.ruoyi.common.mybatis.mapper.BaseMapperX; +import org.apache.ibatis.annotations.Param; +import com.ruoyi.common.core.domain.entity.SysDictData; + +/** + * 字典表 数据层 + * + * @author ruoyi +*/ +@Mapper +public interface SysDictDataMapper extends BaseMapperX +{ + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * @return 字典数据集合信息 + */ + public List selectDictDataList(SysDictData dictData); + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据集合信息 + */ + public List selectDictDataByType(String dictType); + + /** + * 根据字典类型和字典键值查询字典数据信息 + * + * @param dictType 字典类型 + * @param dictValue 字典键值 + * @return 字典标签 + */ + public String selectDictLabel(@Param("dictType") String dictType, @Param("dictValue") String dictValue); + + /** + * 根据字典数据ID查询信息 + * + * @param dictCode 字典数据ID + * @return 字典数据 + */ + public SysDictData selectDictDataById(Long dictCode); + + /** + * 查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据 + */ + public int countDictDataByType(String dictType); + + /** + * 通过字典ID删除字典数据信息 + * + * @param dictCode 字典数据ID + * @return 结果 + */ + public int deleteDictDataById(Long dictCode); + + /** + * 批量删除字典数据信息 + * + * @param dictCodes 需要删除的字典数据ID + * @return 结果 + */ + public int deleteDictDataByIds(Long[] dictCodes); + + /** + * 新增字典数据信息 + * + * @param dictData 字典数据信息 + * @return 结果 + */ + public int insertDictData(SysDictData dictData); + + /** + * 修改字典数据信息 + * + * @param dictData 字典数据信息 + * @return 结果 + */ + public int updateDictData(SysDictData dictData); + + /** + * 同步修改字典类型 + * + * @param oldDictType 旧字典类型 + * @param newDictType 新旧字典类型 + * @return 结果 + */ + public int updateDictDataType(@Param("oldDictType") String oldDictType, @Param("newDictType") String newDictType); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictTypeMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictTypeMapper.java new file mode 100644 index 00000000..25faedd6 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictTypeMapper.java @@ -0,0 +1,87 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Mapper; + +import com.ruoyi.common.mybatis.mapper.BaseMapperX; +import com.ruoyi.common.core.domain.entity.SysDictType; + +/** + * 字典表 数据层 + * + * @author ruoyi +*/ +@Mapper +public interface SysDictTypeMapper extends BaseMapperX +{ + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * @return 字典类型集合信息 + */ + public List selectDictTypeList(SysDictType dictType); + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + public List selectDictTypeAll(); + + /** + * 根据字典类型ID查询信息 + * + * @param dictId 字典类型ID + * @return 字典类型 + */ + public SysDictType selectDictTypeById(Long dictId); + + /** + * 根据字典类型查询信息 + * + * @param dictType 字典类型 + * @return 字典类型 + */ + public SysDictType selectDictTypeByType(String dictType); + + /** + * 通过字典ID删除字典信息 + * + * @param dictId 字典ID + * @return 结果 + */ + public int deleteDictTypeById(Long dictId); + + /** + * 批量删除字典类型信息 + * + * @param dictIds 需要删除的字典ID + * @return 结果 + */ + public int deleteDictTypeByIds(Long[] dictIds); + + /** + * 新增字典类型信息 + * + * @param dictType 字典类型信息 + * @return 结果 + */ + public int insertDictType(SysDictType dictType); + + /** + * 修改字典类型信息 + * + * @param dictType 字典类型信息 + * @return 结果 + */ + public int updateDictType(SysDictType dictType); + + /** + * 校验字典类型称是否唯一 + * + * @param dictType 字典类型 + * @return 结果 + */ + public SysDictType checkDictTypeUnique(String dictType); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysLogininforMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysLogininforMapper.java new file mode 100644 index 00000000..c521eaad --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysLogininforMapper.java @@ -0,0 +1,44 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Mapper; +import com.ruoyi.system.domain.SysLogininfor; + +/** + * 系统访问日志情况信息 数据层 + * + * @author ruoyi +*/ +@Mapper +public interface SysLogininforMapper +{ + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + public void insertLogininfor(SysLogininfor logininfor); + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * @return 登录记录集合 + */ + public List selectLogininforList(SysLogininfor logininfor); + + /** + * 批量删除系统登录日志 + * + * @param infoIds 需要删除的登录日志ID + * @return 结果 + */ + public int deleteLogininforByIds(Long[] infoIds); + + /** + * 清空系统登录日志 + * + * @return 结果 + */ + public int cleanLogininfor(); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java new file mode 100644 index 00000000..838387e1 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java @@ -0,0 +1,136 @@ +package com.ruoyi.system.mapper; + +import java.util.List; + +import com.ruoyi.common.mybatis.query.LambdaQueryWrapperX; +import org.apache.ibatis.annotations.Mapper; + +import com.ruoyi.common.mybatis.mapper.BaseMapperX; +import org.apache.ibatis.annotations.Param; +import com.ruoyi.common.core.domain.entity.SysMenu; + +/** + * 菜单表 数据层 + * + * @author ruoyi +*/ +@Mapper +public interface SysMenuMapper extends BaseMapperX +{ + /** + * 查询系统菜单列表 + * + * @param menu 菜单信息 + * @return 菜单列表 + */ + public List selectMenuList(SysMenu menu); + + /** + * 根据用户所有权限 + * + * @return 权限列表 + */ + public List selectMenuPerms(); + + /** + * 根据用户查询系统菜单列表 + * + * @param menu 菜单信息 + * @return 菜单列表 + */ + public List selectMenuListByUserId(SysMenu menu); + + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * @return 权限列表 + */ + public List selectMenuPermsByRoleId(Long roleId); + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + public List selectMenuPermsByUserId(Long userId); + + /** + * 根据用户ID查询菜单 + * + * @return 菜单列表 + */ + public List selectMenuTreeAll(); + + /** + * 根据用户ID查询菜单 + * + * @param userId 用户ID + * @return 菜单列表 + */ + public List selectMenuTreeByUserId(Long userId); + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @param menuCheckStrictly 菜单树选择项是否关联显示 + * @return 选中菜单列表 + */ + public List selectMenuListByRoleId(@Param("roleId") Long roleId, @Param("menuCheckStrictly") boolean menuCheckStrictly); + + /** + * 根据菜单ID查询信息 + * + * @param menuId 菜单ID + * @return 菜单信息 + */ + public SysMenu selectMenuById(Long menuId); + + /** + * 是否存在菜单子节点 + * + * @param menuId 菜单ID + * @return 结果 + */ + public int hasChildByMenuId(Long menuId); + + /** + * 新增菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + public int insertMenu(SysMenu menu); + + /** + * 修改菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + public int updateMenu(SysMenu menu); + + /** + * 删除菜单管理信息 + * + * @param menuId 菜单ID + * @return 结果 + */ + public int deleteMenuById(Long menuId); + + /** + * 校验菜单名称是否唯一 + * + * @param menuName 菜单名称 + * @param parentId 父菜单ID + * @return 结果 + */ + public SysMenu checkMenuNameUnique(@Param("menuName") String menuName, @Param("parentId") Long parentId); + + default List selectList(SysMenu reqVO) { + return selectList(new LambdaQueryWrapperX().likeIfPresent(SysMenu::getMenuName, reqVO.getMenuName()) + .eqIfPresent(SysMenu::getStatus, reqVO.getStatus()).eqIfPresent(SysMenu::getVisible, reqVO.getVisible())); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysNoticeMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysNoticeMapper.java new file mode 100644 index 00000000..1551e1d8 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysNoticeMapper.java @@ -0,0 +1,62 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Mapper; +import com.ruoyi.system.domain.SysNotice; + +/** + * 通知公告表 数据层 + * + * @author ruoyi +*/ +@Mapper +public interface SysNoticeMapper +{ + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * @return 公告信息 + */ + public SysNotice selectNoticeById(Long noticeId); + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * @return 公告集合 + */ + public List selectNoticeList(SysNotice notice); + + /** + * 新增公告 + * + * @param notice 公告信息 + * @return 结果 + */ + public int insertNotice(SysNotice notice); + + /** + * 修改公告 + * + * @param notice 公告信息 + * @return 结果 + */ + public int updateNotice(SysNotice notice); + + /** + * 批量删除公告 + * + * @param noticeId 公告ID + * @return 结果 + */ + public int deleteNoticeById(Long noticeId); + + /** + * 批量删除公告信息 + * + * @param noticeIds 需要删除的公告ID + * @return 结果 + */ + public int deleteNoticeByIds(Long[] noticeIds); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOperLogMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOperLogMapper.java new file mode 100644 index 00000000..47ac30a7 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOperLogMapper.java @@ -0,0 +1,50 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Mapper; +import com.ruoyi.system.domain.SysOperLog; + +/** + * 操作日志 数据层 + * + * @author ruoyi +*/ +@Mapper +public interface SysOperLogMapper +{ + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + */ + public void insertOperlog(SysOperLog operLog); + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * @return 操作日志集合 + */ + public List selectOperLogList(SysOperLog operLog); + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * @return 结果 + */ + public int deleteOperLogByIds(Long[] operIds); + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * @return 操作日志对象 + */ + public SysOperLog selectOperLogById(Long operId); + + /** + * 清空操作日志 + */ + public void cleanOperLog(); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysPostMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysPostMapper.java new file mode 100644 index 00000000..5de093e2 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysPostMapper.java @@ -0,0 +1,104 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Mapper; + +import com.ruoyi.common.mybatis.mapper.BaseMapperX; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.system.domain.SysPost; + +/** + * 岗位信息 数据层 + * + * @author ruoyi +*/ +@Mapper +public interface SysPostMapper extends BaseMapperX +{ + /** + * 查询岗位数据集合 + * + * @param post 岗位信息 + * @return 岗位数据集合 + */ + public List selectPostList(SysPost post); + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + public List selectPostAll(); + + /** + * 通过岗位ID查询岗位信息 + * + * @param postId 岗位ID + * @return 角色对象信息 + */ + public SysPost selectPostById(Long postId); + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * @return 选中岗位ID列表 + */ + public List selectPostListByUserId(Long userId); + + /** + * 查询用户所属岗位组 + * + * @param userName 用户名 + * @return 结果 + */ + public List selectPostsByUserName(String userName); + + /** + * 删除岗位信息 + * + * @param postId 岗位ID + * @return 结果 + */ + public int deletePostById(Long postId); + + /** + * 批量删除岗位信息 + * + * @param postIds 需要删除的岗位ID + * @return 结果 + */ + public int deletePostByIds(Long[] postIds); + + /** + * 修改岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + public int updatePost(SysPost post); + + /** + * 新增岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + public int insertPost(SysPost post); + + /** + * 校验岗位名称 + * + * @param postName 岗位名称 + * @return 结果 + */ + public SysPost checkPostNameUnique(String postName); + + /** + * 校验岗位编码 + * + * @param postCode 岗位编码 + * @return 结果 + */ + public SysPost checkPostCodeUnique(String postCode); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleDeptMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleDeptMapper.java new file mode 100644 index 00000000..3661a594 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleDeptMapper.java @@ -0,0 +1,48 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Mapper; + +import com.ruoyi.common.mybatis.mapper.BaseMapperX; +import com.ruoyi.system.domain.SysRoleDept; + +/** + * 角色与部门关联表 数据层 + * + * @author ruoyi +*/ +@Mapper +public interface SysRoleDeptMapper extends BaseMapperX +{ + /** + * 通过角色ID删除角色和部门关联 + * + * @param roleId 角色ID + * @return 结果 + */ + public int deleteRoleDeptByRoleId(Long roleId); + + /** + * 批量删除角色部门关联信息 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteRoleDept(Long[] ids); + + /** + * 查询部门使用数量 + * + * @param deptId 部门ID + * @return 结果 + */ + public int selectCountRoleDeptByDeptId(Long deptId); + + /** + * 批量新增角色部门信息 + * + * @param roleDeptList 角色部门列表 + * @return 结果 + */ + public int batchRoleDept(List roleDeptList); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java new file mode 100644 index 00000000..36b1dd65 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java @@ -0,0 +1,111 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Mapper; + +import com.ruoyi.common.mybatis.mapper.BaseMapperX; +import com.ruoyi.common.core.domain.entity.SysRole; + +/** + * 角色表 数据层 + * + * @author ruoyi +*/ +@Mapper +public interface SysRoleMapper extends BaseMapperX +{ + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * @return 角色数据集合信息 + */ + public List selectRoleList(SysRole role); + + /** + * 根据用户ID查询角色 + * + * @param userId 用户ID + * @return 角色列表 + */ + public List selectRolePermissionByUserId(Long userId); + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + public List selectRoleAll(); + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + public List selectRoleListByUserId(Long userId); + + /** + * 通过角色ID查询角色 + * + * @param roleId 角色ID + * @return 角色对象信息 + */ + public SysRole selectRoleById(Long roleId); + + /** + * 根据用户ID查询角色 + * + * @param userName 用户名 + * @return 角色列表 + */ + public List selectRolesByUserName(String userName); + + /** + * 校验角色名称是否唯一 + * + * @param roleName 角色名称 + * @return 角色信息 + */ + public SysRole checkRoleNameUnique(String roleName); + + /** + * 校验角色权限是否唯一 + * + * @param roleKey 角色权限 + * @return 角色信息 + */ + public SysRole checkRoleKeyUnique(String roleKey); + + /** + * 修改角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int updateRole(SysRole role); + + /** + * 新增角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int insertRole(SysRole role); + + /** + * 通过角色ID删除角色 + * + * @param roleId 角色ID + * @return 结果 + */ + public int deleteRoleById(Long roleId); + + /** + * 批量删除角色信息 + * + * @param roleIds 需要删除的角色ID + * @return 结果 + */ + public int deleteRoleByIds(Long[] roleIds); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMenuMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMenuMapper.java new file mode 100644 index 00000000..63e9d415 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMenuMapper.java @@ -0,0 +1,48 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Mapper; + +import com.ruoyi.common.mybatis.mapper.BaseMapperX; +import com.ruoyi.system.domain.SysRoleMenu; + +/** + * 角色与菜单关联表 数据层 + * + * @author ruoyi +*/ +@Mapper +public interface SysRoleMenuMapper extends BaseMapperX +{ + /** + * 查询菜单使用数量 + * + * @param menuId 菜单ID + * @return 结果 + */ + public int checkMenuExistRole(Long menuId); + + /** + * 通过角色ID删除角色和菜单关联 + * + * @param roleId 角色ID + * @return 结果 + */ + public int deleteRoleMenuByRoleId(Long roleId); + + /** + * 批量删除角色菜单关联信息 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteRoleMenu(Long[] ids); + + /** + * 批量新增角色菜单信息 + * + * @param roleMenuList 角色菜单列表 + * @return 结果 + */ + public int batchRoleMenu(List roleMenuList); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysTreeDictDataMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysTreeDictDataMapper.java new file mode 100644 index 00000000..01aa66bc --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysTreeDictDataMapper.java @@ -0,0 +1,33 @@ +package com.ruoyi.system.mapper; + +import com.ruoyi.common.mybatis.mapper.BaseMapperX; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.ruoyi.system.domain.SysTreeDictData; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import org.apache.ibatis.annotations.Mapper; + +/** + * 树形字典数据Mapper接口 +*/ +@Mapper +public interface SysTreeDictDataMapper extends BaseMapperX { + /** + * 分页查询树形字典数据列表 + *

+ * param page 分页信息 + * + * @param entity 树形字典数据信息 + * @return 树形字典数据集合 + */ + public IPage selectTreeDictDataPage(IPage page, @Param("entity") SysTreeDictData entity); + + /** + * 查询所有树形字典数据列表 + * + * @param entity 树形字典数据信息 + * @return 树形字典数据集合 + */ + public List selectTreeDictDataList(@Param("entity") SysTreeDictData entity); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysTreeDictMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysTreeDictMapper.java new file mode 100644 index 00000000..2a04574d --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysTreeDictMapper.java @@ -0,0 +1,34 @@ +package com.ruoyi.system.mapper; + +import com.ruoyi.common.mybatis.mapper.BaseMapperX; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.ruoyi.system.domain.SysTreeDict; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import org.apache.ibatis.annotations.Mapper; + +/** + * 系统树形Mapper接口 +*/ +@Mapper +public interface SysTreeDictMapper extends BaseMapperX { + /** + * 分页查询系统树形列表 + *

+ * param page 分页信息 + * param entity 实体 + * + * @param sysTreeDict 系统树形信息 + * @return 系统树形集合 + */ + public IPage selectTreeDictPage(IPage page, @Param("entity") SysTreeDict sysTreeDict); + + /** + * 查询所有系统树形列表 + * + * @param entity 系统树形信息 + * @return 系统树形集合 + */ + public List selectTreeDictList(@Param("entity") SysTreeDict entity); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java new file mode 100644 index 00000000..d4884688 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java @@ -0,0 +1,133 @@ +package com.ruoyi.system.mapper; + +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.mybatis.mapper.BaseMapperX; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 用户表 数据层 + * + * @author ruoyi + */ +@Mapper +public interface SysUserMapper extends BaseMapperX { + /** + * 根据条件分页查询用户列表 + * + * @param sysUser 用户信息 + * @return 用户信息集合信息 + */ + public List selectUserList(SysUser sysUser); + + /** + * 根据条件分页查询已配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + public List selectAllocatedList(SysUser user); + + /** + * 根据条件分页查询未分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + public List selectUnallocatedList(SysUser user); + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + public SysUser selectUserByUserName(String userName); + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + public SysUser selectUserById(Long userId); + + /** + * 新增用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int insertUser(SysUser user); + + /** + * 修改用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUser(SysUser user); + + /** + * 修改用户头像 + * + * @param userName 用户名 + * @param avatar 头像地址 + * @return 结果 + */ + public int updateUserAvatar(@Param("userName") String userName, @Param("avatar") String avatar); + + /** + * 重置用户密码 + * + * @param userName 用户名 + * @param password 密码 + * @return 结果 + */ + public int resetUserPwd(@Param("userName") String userName, @Param("password") String password); + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * @return 结果 + */ + public int deleteUserById(Long userId); + + /** + * 批量删除用户信息 + * + * @param userIds 需要删除的用户ID + * @return 结果 + */ + public int deleteUserByIds(Long[] userIds); + + /** + * 校验用户名称是否唯一 + * + * @param userName 用户名称 + * @return 结果 + */ + public SysUser checkUserNameUnique(String userName); + + /** + * 校验手机号码是否唯一 + * + * @param phonenumber 手机号码 + * @return 结果 + */ + public SysUser checkPhoneUnique(String phonenumber); + + /** + * 校验email是否唯一 + * + * @param email 用户邮箱 + * @return 结果 + */ + public SysUser checkEmailUnique(String email); + + default List selectListByStatus(Integer status) { + return selectList(SysUser::getStatus, status); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserPostMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserPostMapper.java new file mode 100644 index 00000000..78bb6e8f --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserPostMapper.java @@ -0,0 +1,48 @@ +package com.ruoyi.system.mapper; + +import com.ruoyi.common.mybatis.mapper.BaseMapperX; +import com.ruoyi.system.domain.SysUserPost; + +import java.util.List; +import org.apache.ibatis.annotations.Mapper; + +/** + * 用户与岗位关联表 数据层 + * + * @author ruoyi +*/ +@Mapper +public interface SysUserPostMapper extends BaseMapperX +{ + /** + * 通过用户ID删除用户和岗位关联 + * + * @param userId 用户ID + * @return 结果 + */ + public int deleteUserPostByUserId(Long userId); + + /** + * 通过岗位ID查询岗位使用数量 + * + * @param postId 岗位ID + * @return 结果 + */ + public int countUserPostById(Long postId); + + /** + * 批量删除用户和岗位关联 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteUserPost(Long[] ids); + + /** + * 批量新增用户岗位信息 + * + * @param userPostList 用户角色列表 + * @return 结果 + */ + public int batchUserPost(List userPostList); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserRoleMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserRoleMapper.java new file mode 100644 index 00000000..980790fe --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserRoleMapper.java @@ -0,0 +1,66 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Mapper; + +import com.ruoyi.common.mybatis.mapper.BaseMapperX; +import org.apache.ibatis.annotations.Param; +import com.ruoyi.system.domain.SysUserRole; + +/** + * 用户与角色关联表 数据层 + * + * @author ruoyi +*/ +@Mapper +public interface SysUserRoleMapper extends BaseMapperX +{ + /** + * 通过用户ID删除用户和角色关联 + * + * @param userId 用户ID + * @return 结果 + */ + public int deleteUserRoleByUserId(Long userId); + + /** + * 批量删除用户和角色关联 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteUserRole(Long[] ids); + + /** + * 通过角色ID查询角色使用数量 + * + * @param roleId 角色ID + * @return 结果 + */ + public int countUserRoleByRoleId(Long roleId); + + /** + * 批量新增用户角色信息 + * + * @param userRoleList 用户角色列表 + * @return 结果 + */ + public int batchUserRole(List userRoleList); + + /** + * 删除用户和角色关联信息 + * + * @param userRole 用户和角色关联信息 + * @return 结果 + */ + public int deleteUserRoleInfo(SysUserRole userRole); + + /** + * 批量取消授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要删除的用户数据ID + * @return 结果 + */ + public int deleteUserRoleInfos(@Param("roleId") Long roleId, @Param("userIds") Long[] userIds); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TenantMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TenantMapper.java new file mode 100644 index 00000000..94f8e27b --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TenantMapper.java @@ -0,0 +1,47 @@ +package com.ruoyi.system.mapper; + + +import com.ruoyi.common.mybatis.mapper.BaseMapperX; +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.common.mybatis.query.LambdaQueryWrapperX; +import com.ruoyi.system.domain.TenantDO; +import com.ruoyi.system.domain.vo.tenant.TenantExportReqVO; +import com.ruoyi.system.domain.vo.tenant.TenantPageReqVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; + +import java.util.Date; +import java.util.List; + +/** + * 租户 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface TenantMapper extends BaseMapperX { + + default PageResult selectPage(TenantPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX().likeIfPresent(TenantDO::getName, reqVO.getName()).likeIfPresent(TenantDO::getContactName, reqVO.getContactName()).likeIfPresent(TenantDO::getContactMobile, reqVO.getContactMobile()).eqIfPresent(TenantDO::getStatus, reqVO.getStatus()).betweenIfPresent(TenantDO::getCreateTime, reqVO.getCreateTime()).orderByDesc(TenantDO::getId)); + } + + default List selectList(TenantExportReqVO reqVO) { + return selectList(new LambdaQueryWrapperX().likeIfPresent(TenantDO::getName, reqVO.getName()).likeIfPresent(TenantDO::getContactName, reqVO.getContactName()).likeIfPresent(TenantDO::getContactMobile, reqVO.getContactMobile()).eqIfPresent(TenantDO::getStatus, reqVO.getStatus()).betweenIfPresent(TenantDO::getCreateTime, reqVO.getCreateTime()).orderByDesc(TenantDO::getId)); + } + + default TenantDO selectByName(String name) { + return selectOne(TenantDO::getName, name); + } + + default Long selectCountByPackageId(Long packageId) { + return selectCount(TenantDO::getPackageId, packageId); + } + + default List selectListByPackageId(Long packageId) { + return selectList(TenantDO::getPackageId, packageId); + } + + @Select("SELECT COUNT(*) FROM system_tenant WHERE update_time > #{maxUpdateTime}") + Long selectCountByUpdateTimeGt(Date maxUpdateTime); + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TenantPackageMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TenantPackageMapper.java new file mode 100644 index 00000000..141fe76f --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TenantPackageMapper.java @@ -0,0 +1,28 @@ +package com.ruoyi.system.mapper; + + +import com.ruoyi.common.mybatis.mapper.BaseMapperX; +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.common.mybatis.query.LambdaQueryWrapperX; +import com.ruoyi.system.domain.TenantPackageDO; +import com.ruoyi.system.domain.vo.packages.TenantPackagePageReqVO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 租户套餐 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface TenantPackageMapper extends BaseMapperX { + + default PageResult selectPage(TenantPackagePageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX().likeIfPresent(TenantPackageDO::getName, reqVO.getName()).eqIfPresent(TenantPackageDO::getStatus, reqVO.getStatus()).likeIfPresent(TenantPackageDO::getRemark, reqVO.getRemark()).betweenIfPresent(TenantPackageDO::getCreateTime, reqVO.getCreateTime()).orderByDesc(TenantPackageDO::getId)); + } + + default List selectListByStatus(Integer status) { + return selectList(TenantPackageDO::getStatus, status); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java new file mode 100644 index 00000000..58bdc292 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java @@ -0,0 +1,89 @@ +package com.ruoyi.system.service; + +import java.util.List; +import com.ruoyi.system.domain.SysConfig; + +/** + * 参数配置 服务层 + * + * @author ruoyi + */ +public interface ISysConfigService +{ + /** + * 查询参数配置信息 + * + * @param configId 参数配置ID + * @return 参数配置信息 + */ + public SysConfig selectConfigById(Long configId); + + /** + * 根据键名查询参数配置信息 + * + * @param configKey 参数键名 + * @return 参数键值 + */ + public String selectConfigByKey(String configKey); + + /** + * 获取验证码开关 + * + * @return true开启,false关闭 + */ + public boolean selectCaptchaEnabled(); + + /** + * 查询参数配置列表 + * + * @param config 参数配置信息 + * @return 参数配置集合 + */ + public List selectConfigList(SysConfig config); + + /** + * 新增参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + public int insertConfig(SysConfig config); + + /** + * 修改参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + public int updateConfig(SysConfig config); + + /** + * 批量删除参数信息 + * + * @param configIds 需要删除的参数ID + */ + public void deleteConfigByIds(Long[] configIds); + + /** + * 加载参数缓存数据 + */ + public void loadingConfigCache(); + + /** + * 清空参数缓存数据 + */ + public void clearConfigCache(); + + /** + * 重置参数缓存数据 + */ + public void resetConfigCache(); + + /** + * 校验参数键名是否唯一 + * + * @param config 参数信息 + * @return 结果 + */ + public String checkConfigKeyUnique(SysConfig config); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java new file mode 100644 index 00000000..c4e68eb8 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java @@ -0,0 +1,126 @@ +package com.ruoyi.system.service; + +import java.util.List; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.common.core.domain.TreeSelect; +import com.ruoyi.common.core.domain.entity.SysDept; + +/** + * 部门管理 服务层 + * + * @author ruoyi + */ +public interface ISysDeptService extends IService +{ + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * @return 部门信息集合 + */ + public List selectDeptList(SysDept dept); + + /** + * 查询部门树结构信息 + * + * @param dept 部门信息 + * @return 部门树信息集合 + */ + public List selectDeptTreeList(SysDept dept); + + /** + * 构建前端所需要树结构 + * + * @param depts 部门列表 + * @return 树结构列表 + */ + public List buildDeptTree(List depts); + + /** + * 构建前端所需要下拉树结构 + * + * @param depts 部门列表 + * @return 下拉树结构列表 + */ + public List buildDeptTreeSelect(List depts); + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @return 选中部门列表 + */ + public List selectDeptListByRoleId(Long roleId); + + /** + * 根据部门ID查询信息 + * + * @param deptId 部门ID + * @return 部门信息 + */ + public SysDept selectDeptById(Long deptId); + + /** + * 根据ID查询所有子部门(正常状态) + * + * @param deptId 部门ID + * @return 子部门数 + */ + public int selectNormalChildrenDeptById(Long deptId); + + /** + * 是否存在部门子节点 + * + * @param deptId 部门ID + * @return 结果 + */ + public boolean hasChildByDeptId(Long deptId); + + /** + * 查询部门是否存在用户 + * + * @param deptId 部门ID + * @return 结果 true 存在 false 不存在 + */ + public boolean checkDeptExistUser(Long deptId); + + /** + * 校验部门名称是否唯一 + * + * @param dept 部门信息 + * @return 结果 + */ + public String checkDeptNameUnique(SysDept dept); + + /** + * 校验部门是否有数据权限 + * + * @param deptId 部门id + */ + public void checkDeptDataScope(Long deptId); + + /** + * 新增保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + public int insertDept(SysDept dept); + + /** + * 修改保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + public int updateDept(SysDept dept); + + /** + * 删除部门管理信息 + * + * @param deptId 部门ID + * @return 结果 + */ + public int deleteDeptById(Long deptId); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictDataService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictDataService.java new file mode 100644 index 00000000..23d18826 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictDataService.java @@ -0,0 +1,62 @@ +package com.ruoyi.system.service; + +import java.util.List; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.common.core.domain.entity.SysDictData; + +/** + * 字典 业务层 + * + * @author ruoyi + */ +public interface ISysDictDataService extends IService +{ + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * @return 字典数据集合信息 + */ + public List selectDictDataList(SysDictData dictData); + + /** + * 根据字典类型和字典键值查询字典数据信息 + * + * @param dictType 字典类型 + * @param dictValue 字典键值 + * @return 字典标签 + */ + public String selectDictLabel(String dictType, String dictValue); + + /** + * 根据字典数据ID查询信息 + * + * @param dictCode 字典数据ID + * @return 字典数据 + */ + public SysDictData selectDictDataById(Long dictCode); + + /** + * 批量删除字典数据信息 + * + * @param dictCodes 需要删除的字典数据ID + */ + public void deleteDictDataByIds(Long[] dictCodes); + + /** + * 新增保存字典数据信息 + * + * @param dictData 字典数据信息 + * @return 结果 + */ + public int insertDictData(SysDictData dictData); + + /** + * 修改保存字典数据信息 + * + * @param dictData 字典数据信息 + * @return 结果 + */ + public int updateDictData(SysDictData dictData); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java new file mode 100644 index 00000000..31c60f76 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java @@ -0,0 +1,100 @@ +package com.ruoyi.system.service; + +import java.util.List; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.common.core.domain.entity.SysDictData; +import com.ruoyi.common.core.domain.entity.SysDictType; + +/** + * 字典 业务层 + * + * @author ruoyi + */ +public interface ISysDictTypeService extends IService +{ + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * @return 字典类型集合信息 + */ + public List selectDictTypeList(SysDictType dictType); + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + public List selectDictTypeAll(); + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据集合信息 + */ + public List selectDictDataByType(String dictType); + + /** + * 根据字典类型ID查询信息 + * + * @param dictId 字典类型ID + * @return 字典类型 + */ + public SysDictType selectDictTypeById(Long dictId); + + /** + * 根据字典类型查询信息 + * + * @param dictType 字典类型 + * @return 字典类型 + */ + public SysDictType selectDictTypeByType(String dictType); + + /** + * 批量删除字典信息 + * + * @param dictIds 需要删除的字典ID + */ + public void deleteDictTypeByIds(Long[] dictIds); + + /** + * 加载字典缓存数据 + */ + public void loadingDictCache(); + + /** + * 清空字典缓存数据 + */ + public void clearDictCache(); + + /** + * 重置字典缓存数据 + */ + public void resetDictCache(); + + /** + * 新增保存字典类型信息 + * + * @param dictType 字典类型信息 + * @return 结果 + */ + public int insertDictType(SysDictType dictType); + + /** + * 修改保存字典类型信息 + * + * @param dictType 字典类型信息 + * @return 结果 + */ + public int updateDictType(SysDictType dictType); + + /** + * 校验字典类型称是否唯一 + * + * @param dictType 字典类型 + * @return 结果 + */ + public String checkDictTypeUnique(SysDictType dictType); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysLogininforService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysLogininforService.java new file mode 100644 index 00000000..ce3151d3 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysLogininforService.java @@ -0,0 +1,40 @@ +package com.ruoyi.system.service; + +import java.util.List; +import com.ruoyi.system.domain.SysLogininfor; + +/** + * 系统访问日志情况信息 服务层 + * + * @author ruoyi + */ +public interface ISysLogininforService +{ + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + public void insertLogininfor(SysLogininfor logininfor); + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * @return 登录记录集合 + */ + public List selectLogininforList(SysLogininfor logininfor); + + /** + * 批量删除系统登录日志 + * + * @param infoIds 需要删除的登录日志ID + * @return 结果 + */ + public int deleteLogininforByIds(Long[] infoIds); + + /** + * 清空系统登录日志 + */ + public void cleanLogininfor(); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java new file mode 100644 index 00000000..e9d3479f --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java @@ -0,0 +1,148 @@ +package com.ruoyi.system.service; + +import java.util.List; +import java.util.Set; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.common.core.domain.TreeSelect; +import com.ruoyi.common.core.domain.entity.SysMenu; +import com.ruoyi.system.domain.vo.RouterVo; + +/** + * 菜单 业务层 + * + * @author ruoyi + */ +public interface ISysMenuService extends IService +{ + /** + * 根据用户查询系统菜单列表 + * + * @param userId 用户ID + * @return 菜单列表 + */ + public List selectMenuList(Long userId); + + /** + * 根据用户查询系统菜单列表 + * + * @param menu 菜单信息 + * @param userId 用户ID + * @return 菜单列表 + */ + public List selectMenuList(SysMenu menu, Long userId); + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + public Set selectMenuPermsByUserId(Long userId); + + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * @return 权限列表 + */ + public Set selectMenuPermsByRoleId(Long roleId); + + /** + * 根据用户ID查询菜单树信息 + * + * @param userId 用户ID + * @return 菜单列表 + */ + public List selectMenuTreeByUserId(Long userId); + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @return 选中菜单列表 + */ + public List selectMenuListByRoleId(Long roleId); + + /** + * 构建前端路由所需要的菜单 + * + * @param menus 菜单列表 + * @return 路由列表 + */ + public List buildMenus(List menus); + + /** + * 构建前端所需要树结构 + * + * @param menus 菜单列表 + * @return 树结构列表 + */ + public List buildMenuTree(List menus); + + /** + * 构建前端所需要下拉树结构 + * + * @param menus 菜单列表 + * @return 下拉树结构列表 + */ + public List buildMenuTreeSelect(List menus); + + /** + * 根据菜单ID查询信息 + * + * @param menuId 菜单ID + * @return 菜单信息 + */ + public SysMenu selectMenuById(Long menuId); + + /** + * 是否存在菜单子节点 + * + * @param menuId 菜单ID + * @return 结果 true 存在 false 不存在 + */ + public boolean hasChildByMenuId(Long menuId); + + /** + * 查询菜单是否存在角色 + * + * @param menuId 菜单ID + * @return 结果 true 存在 false 不存在 + */ + public boolean checkMenuExistRole(Long menuId); + + /** + * 新增保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + public int insertMenu(SysMenu menu); + + /** + * 修改保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + public int updateMenu(SysMenu menu); + + /** + * 删除菜单管理信息 + * + * @param menuId 菜单ID + * @return 结果 + */ + public int deleteMenuById(Long menuId); + + /** + * 校验菜单名称是否唯一 + * + * @param menu 菜单信息 + * @return 结果 + */ + public String checkMenuNameUnique(SysMenu menu); + + List getTenantMenus(SysMenu sysMenu); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java new file mode 100644 index 00000000..47ce1b71 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java @@ -0,0 +1,60 @@ +package com.ruoyi.system.service; + +import java.util.List; +import com.ruoyi.system.domain.SysNotice; + +/** + * 公告 服务层 + * + * @author ruoyi + */ +public interface ISysNoticeService +{ + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * @return 公告信息 + */ + public SysNotice selectNoticeById(Long noticeId); + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * @return 公告集合 + */ + public List selectNoticeList(SysNotice notice); + + /** + * 新增公告 + * + * @param notice 公告信息 + * @return 结果 + */ + public int insertNotice(SysNotice notice); + + /** + * 修改公告 + * + * @param notice 公告信息 + * @return 结果 + */ + public int updateNotice(SysNotice notice); + + /** + * 删除公告信息 + * + * @param noticeId 公告ID + * @return 结果 + */ + public int deleteNoticeById(Long noticeId); + + /** + * 批量删除公告信息 + * + * @param noticeIds 需要删除的公告ID + * @return 结果 + */ + public int deleteNoticeByIds(Long[] noticeIds); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOperLogService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOperLogService.java new file mode 100644 index 00000000..4fd8e5a8 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOperLogService.java @@ -0,0 +1,48 @@ +package com.ruoyi.system.service; + +import java.util.List; +import com.ruoyi.system.domain.SysOperLog; + +/** + * 操作日志 服务层 + * + * @author ruoyi + */ +public interface ISysOperLogService +{ + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + */ + public void insertOperlog(SysOperLog operLog); + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * @return 操作日志集合 + */ + public List selectOperLogList(SysOperLog operLog); + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * @return 结果 + */ + public int deleteOperLogByIds(Long[] operIds); + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * @return 操作日志对象 + */ + public SysOperLog selectOperLogById(Long operId); + + /** + * 清空操作日志 + */ + public void cleanOperLog(); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysPostService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysPostService.java new file mode 100644 index 00000000..7bfe2ecf --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysPostService.java @@ -0,0 +1,101 @@ +package com.ruoyi.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.system.domain.SysPost; + +import java.util.List; + +/** + * 岗位信息 服务层 + * + * @author ruoyi + */ +public interface ISysPostService extends IService +{ + /** + * 查询岗位信息集合 + * + * @param post 岗位信息 + * @return 岗位列表 + */ + public List selectPostList(SysPost post); + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + public List selectPostAll(); + + /** + * 通过岗位ID查询岗位信息 + * + * @param postId 岗位ID + * @return 角色对象信息 + */ + public SysPost selectPostById(Long postId); + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * @return 选中岗位ID列表 + */ + public List selectPostListByUserId(Long userId); + + /** + * 校验岗位名称 + * + * @param post 岗位信息 + * @return 结果 + */ + public String checkPostNameUnique(SysPost post); + + /** + * 校验岗位编码 + * + * @param post 岗位信息 + * @return 结果 + */ + public String checkPostCodeUnique(SysPost post); + + /** + * 通过岗位ID查询岗位使用数量 + * + * @param postId 岗位ID + * @return 结果 + */ + public int countUserPostById(Long postId); + + /** + * 删除岗位信息 + * + * @param postId 岗位ID + * @return 结果 + */ + public int deletePostById(Long postId); + + /** + * 批量删除岗位信息 + * + * @param postIds 需要删除的岗位ID + * @return 结果 + */ + public int deletePostByIds(Long[] postIds); + + /** + * 新增保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + public int insertPost(SysPost post); + + /** + * 修改保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + public int updatePost(SysPost post); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java new file mode 100644 index 00000000..d0e63b23 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java @@ -0,0 +1,179 @@ +package com.ruoyi.system.service; + +import java.util.List; +import java.util.Set; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.system.domain.SysUserRole; + +/** + * 角色业务层 + * + * @author ruoyi + */ +public interface ISysRoleService extends IService +{ + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * @return 角色数据集合信息 + */ + public List selectRoleList(SysRole role); + + /** + * 根据用户ID查询角色列表 + * + * @param userId 用户ID + * @return 角色列表 + */ + public List selectRolesByUserId(Long userId); + + /** + * 根据用户ID查询角色权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + public Set selectRolePermissionByUserId(Long userId); + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + public List selectRoleAll(); + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + public List selectRoleListByUserId(Long userId); + + /** + * 通过角色ID查询角色 + * + * @param roleId 角色ID + * @return 角色对象信息 + */ + public SysRole selectRoleById(Long roleId); + + /** + * 校验角色名称是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + public String checkRoleNameUnique(SysRole role); + + /** + * 校验角色权限是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + public String checkRoleKeyUnique(SysRole role); + + /** + * 校验角色是否允许操作 + * + * @param role 角色信息 + */ + public void checkRoleAllowed(SysRole role); + + /** + * 校验角色是否有数据权限 + * + * @param roleId 角色id + */ + public void checkRoleDataScope(Long roleId); + + /** + * 通过角色ID查询角色使用数量 + * + * @param roleId 角色ID + * @return 结果 + */ + public int countUserRoleByRoleId(Long roleId); + + /** + * 新增保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int insertRole(SysRole role); + + /** + * 修改保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int updateRole(SysRole role); + + /** + * 修改角色状态 + * + * @param role 角色信息 + * @return 结果 + */ + public int updateRoleStatus(SysRole role); + + /** + * 修改数据权限信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int authDataScope(SysRole role); + + /** + * 通过角色ID删除角色 + * + * @param roleId 角色ID + * @return 结果 + */ + public int deleteRoleById(Long roleId); + + /** + * 批量删除角色信息 + * + * @param roleIds 需要删除的角色ID + * @return 结果 + */ + public int deleteRoleByIds(Long[] roleIds); + + /** + * 取消授权用户角色 + * + * @param userRole 用户和角色关联信息 + * @return 结果 + */ + public int deleteAuthUser(SysUserRole userRole); + + /** + * 批量取消授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要取消授权的用户数据ID + * @return 结果 + */ + public int deleteAuthUsers(Long roleId, Long[] userIds); + + /** + * 批量选择授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要删除的用户数据ID + * @return 结果 + */ + public int insertAuthUsers(Long roleId, Long[] userIds); + + SysRole selectByRoleKey(String roleKey); + + Set getRoleMenuIds(Long roleId); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysTreeDictDataService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysTreeDictDataService.java new file mode 100644 index 00000000..ad017f4d --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysTreeDictDataService.java @@ -0,0 +1,107 @@ +package com.ruoyi.system.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.system.domain.SysTreeDict; +import com.ruoyi.system.domain.SysTreeDictData; + +import java.util.List; + +/** + * 树形字典数据Service接口 + * + * @author wangzongrun + * @date 2021-05-31 + */ +public interface ISysTreeDictDataService { + /** + * 查询树形字典数据 + * + * @param id 树形字典数据ID + * @return 树形字典数据 + */ + SysTreeDictData selectById(String id); + + /** + * ` + * 分页查询树形字典数据列表 + * + * @param page 分页 + * @param sysTreeDictData 树形字典数据 + * @return 树形字典数据集合 + */ + IPage selectList(IPage page, SysTreeDictData sysTreeDictData); + + /** + * 查询所有树形字典数据列表 + * + * @param sysTreeDictData 树形字典数据 + * @return 树形字典数据集合 + */ + List selectListAll(SysTreeDictData sysTreeDictData); + + /** + * 查询所有树形字典数据树结构 + * + * @param sysTreeDictData 树形字典数据 + * @return 树结构 + */ + List buildTree(SysTreeDictData sysTreeDictData); + + /** + * 新增树形字典数据 + * + * @param sysTreeDictData 树形字典数据 + * @return 结果 + */ + int insert(SysTreeDictData sysTreeDictData); + + /** + * 修改树形字典数据 + * + * @param sysTreeDictData 树形字典数据 + * @return 结果 + */ + int update(SysTreeDictData sysTreeDictData); + + /** + * 批量删除树形字典数据 + * + * @param sids 需要删除的树形字典数据ID + * @return 结果 + */ + int deleteByIds(String[] sids); + + /** + * 删除树形字典数据信息 + * + * @param id 树形字典数据ID + * @return 结果 + */ + int deleteById(String id); + + /** + * 检查名称唯一性 + * + * @param entity 查询条件 + * @return 结果 + */ + AjaxResult checkUniqueByLabel(SysTreeDictData entity); + + + /** + * 检查编码唯一性 + * + * @param treeDict 查询条件 + * @return 结果 + */ + List selectTreeDictByType(String treeDict); + + /** + * 校验编码 + * + * @param sysTreeDictData 查询条件 + * @return 结果 + */ + AjaxResult checkCode(SysTreeDictData sysTreeDictData); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysTreeDictService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysTreeDictService.java new file mode 100644 index 00000000..d705601e --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysTreeDictService.java @@ -0,0 +1,70 @@ +package com.ruoyi.system.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.ruoyi.system.domain.SysTreeDict; + +import java.util.List; + +/** + * 系统树形Service接口 + * + * @author wangzongrun + * @date 2021-06-04 + */ +public interface ISysTreeDictService { + /** + * 查询系统树形 + * + * @param id 系统树形ID + * @return 系统树形 + */ + public SysTreeDict selectById(String id); + + /** + * 分页查询系统树形列表 + * + * @param sysTreeDict 系统树形 + * @return 系统树形集合 + */ + public IPage selectList(IPage page, SysTreeDict sysTreeDict); + + /** + * 查询所有系统树形列表 + * + * @param sysTreeDict 系统树形 + * @return 系统树形集合 + */ + public List selectListAll(SysTreeDict sysTreeDict); + + /** + * 新增系统树形 + * + * @param sysTreeDict 系统树形 + * @return 结果 + */ + public int insert(SysTreeDict sysTreeDict); + + /** + * 修改系统树形 + * + * @param sysTreeDict 系统树形 + * @return 结果 + */ + public int update(SysTreeDict sysTreeDict); + + /** + * 批量删除系统树形 + * + * @param sids 需要删除的系统树形ID + * @return 结果 + */ + public int deleteByIds(String[] sids); + + /** + * 删除系统树形信息 + * + * @param id 系统树形ID + * @return 结果 + */ + public int deleteById(String id); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserOnlineService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserOnlineService.java new file mode 100644 index 00000000..8eb5448c --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserOnlineService.java @@ -0,0 +1,48 @@ +package com.ruoyi.system.service; + +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.system.domain.SysUserOnline; + +/** + * 在线用户 服务层 + * + * @author ruoyi + */ +public interface ISysUserOnlineService +{ + /** + * 通过登录地址查询信息 + * + * @param ipaddr 登录地址 + * @param user 用户信息 + * @return 在线用户信息 + */ + public SysUserOnline selectOnlineByIpaddr(String ipaddr, LoginUser user); + + /** + * 通过用户名称查询信息 + * + * @param userName 用户名称 + * @param user 用户信息 + * @return 在线用户信息 + */ + public SysUserOnline selectOnlineByUserName(String userName, LoginUser user); + + /** + * 通过登录地址/用户名称查询信息 + * + * @param ipaddr 登录地址 + * @param userName 用户名称 + * @param user 用户信息 + * @return 在线用户信息 + */ + public SysUserOnline selectOnlineByInfo(String ipaddr, String userName, LoginUser user); + + /** + * 设置在线用户信息 + * + * @param user 用户信息 + * @return 在线用户 + */ + public SysUserOnline loginUserToUserOnline(LoginUser user); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java new file mode 100644 index 00000000..2f9d390d --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java @@ -0,0 +1,227 @@ +package com.ruoyi.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.common.core.domain.entity.SysUser; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +/** + * 用户 业务层 + * + * @author ruoyi + */ +public interface ISysUserService extends IService { + /** + * 根据条件分页查询用户列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + public List selectUserList(SysUser user); + + /** + * 根据条件分页查询已分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + public List selectAllocatedList(SysUser user); + + /** + * 根据条件分页查询未分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + public List selectUnallocatedList(SysUser user); + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + public SysUser selectUserByUserName(String userName); + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + public SysUser selectUserById(Long userId); + + /** + * 根据用户ID查询用户所属角色组 + * + * @param userName 用户名 + * @return 结果 + */ + public String selectUserRoleGroup(String userName); + + /** + * 根据用户ID查询用户所属岗位组 + * + * @param userName 用户名 + * @return 结果 + */ + public String selectUserPostGroup(String userName); + + /** + * 校验用户名称是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + public String checkUserNameUnique(SysUser user); + + /** + * 校验手机号码是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + public String checkPhoneUnique(SysUser user); + + /** + * 校验email是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + public String checkEmailUnique(SysUser user); + + /** + * 校验用户是否允许操作 + * + * @param user 用户信息 + */ + public void checkUserAllowed(SysUser user); + + /** + * 校验用户是否有数据权限 + * + * @param userId 用户id + */ + public void checkUserDataScope(Long userId); + + /** + * 新增用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int insertUser(SysUser user); + + /** + * 注册用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public boolean registerUser(SysUser user); + + /** + * 修改用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUser(SysUser user); + + /** + * 用户授权角色 + * + * @param userId 用户ID + * @param roleIds 角色组 + */ + public void insertUserAuth(Long userId, Long[] roleIds); + + /** + * 修改用户状态 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUserStatus(SysUser user); + + /** + * 修改用户基本信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUserProfile(SysUser user); + + /** + * 修改用户头像 + * + * @param userName 用户名 + * @param avatar 头像地址 + * @return 结果 + */ + public boolean updateUserAvatar(String userName, String avatar); + + /** + * 重置用户密码 + * + * @param user 用户信息 + * @return 结果 + */ + public int resetPwd(SysUser user); + + /** + * 重置用户密码 + * + * @param userName 用户名 + * @param password 密码 + * @return 结果 + */ + public int resetUserPwd(String userName, String password); + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * @return 结果 + */ + public int deleteUserById(Long userId); + + /** + * 批量删除用户信息 + * + * @param userIds 需要删除的用户ID + * @return 结果 + */ + public int deleteUserByIds(Long[] userIds); + + /** + * 导入用户数据 + * + * @param userList 用户数据列表 + * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据 + * @param operName 操作用户 + * @return 结果 + */ + public String importUser(List userList, Boolean isUpdateSupport, String operName); + + /** + * 获得指定岗位的用户数组 + * + * @param postIds 岗位数组 + * @return 用户数组 + */ + List getUsersByPostIds(Collection postIds); + + /** + * 获得拥有多个角色的用户编号集合 + * + * @param roleIds 角色编号集合 + * @return 用户编号集合 + */ + Set getUserRoleIdListByRoleIds(Collection roleIds); + + List getUserListByStatus(Integer status); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/TenantPackageService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/TenantPackageService.java new file mode 100644 index 00000000..9b7820f7 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/TenantPackageService.java @@ -0,0 +1,74 @@ +package com.ruoyi.system.service; + + +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.system.domain.TenantPackageDO; +import com.ruoyi.system.domain.vo.packages.TenantPackageCreateReqVO; +import com.ruoyi.system.domain.vo.packages.TenantPackagePageReqVO; +import com.ruoyi.system.domain.vo.packages.TenantPackageUpdateReqVO; + +import javax.validation.Valid; +import java.util.List; + +/** + * 租户套餐 Service 接口 + * + * @author 芋道源码 + */ +public interface TenantPackageService { + + /** + * 创建租户套餐 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createTenantPackage(@Valid TenantPackageCreateReqVO createReqVO); + + /** + * 更新租户套餐 + * + * @param updateReqVO 更新信息 + */ + void updateTenantPackage(@Valid TenantPackageUpdateReqVO updateReqVO); + + /** + * 删除租户套餐 + * + * @param id 编号 + */ + void deleteTenantPackage(Long id); + + /** + * 获得租户套餐 + * + * @param id 编号 + * @return 租户套餐 + */ + TenantPackageDO getTenantPackage(Long id); + + /** + * 获得租户套餐分页 + * + * @param pageReqVO 分页查询 + * @return 租户套餐分页 + */ + PageResult getTenantPackagePage(TenantPackagePageReqVO pageReqVO); + + /** + * 校验租户套餐 + * + * @param id 编号 + * @return 租户套餐 + */ + TenantPackageDO validTenantPackage(Long id); + + /** + * 获得指定状态的租户套餐列表 + * + * @param status 状态 + * @return 租户套餐 + */ + List getTenantPackageListByStatus(Integer status); + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/TenantService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/TenantService.java new file mode 100644 index 00000000..377b399b --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/TenantService.java @@ -0,0 +1,131 @@ +package com.ruoyi.system.service; + + +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.system.domain.TenantDO; +import com.ruoyi.system.domain.vo.tenant.TenantCreateReqVO; +import com.ruoyi.system.domain.vo.tenant.TenantExportReqVO; +import com.ruoyi.system.domain.vo.tenant.TenantPageReqVO; +import com.ruoyi.system.domain.vo.tenant.TenantUpdateReqVO; +import com.ruoyi.system.service.handler.TenantInfoHandler; +import com.ruoyi.system.service.handler.TenantMenuHandler; + +import javax.validation.Valid; +import java.util.List; +import java.util.Set; + +/** + * 租户 Service 接口 + * + * @author 芋道源码 + */ +public interface TenantService { + + /** + * 创建租户 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createTenant(@Valid TenantCreateReqVO createReqVO); + + /** + * 更新租户 + * + * @param updateReqVO 更新信息 + */ + void updateTenant(@Valid TenantUpdateReqVO updateReqVO); + + /** + * 更新租户的角色菜单 + * + * @param tenantId 租户编号 + * @param menuIds 菜单编号数组 + */ + void updateTenantRoleMenu(Long tenantId, Set menuIds); + + /** + * 删除租户 + * + * @param id 编号 + */ + void deleteTenant(Long id); + + /** + * 获得租户 + * + * @param id 编号 + * @return 租户 + */ + TenantDO getTenant(Long id); + + /** + * 获得租户分页 + * + * @param pageReqVO 分页查询 + * @return 租户分页 + */ + PageResult getTenantPage(TenantPageReqVO pageReqVO); + + /** + * 获得租户列表, 用于 Excel 导出 + * + * @param exportReqVO 查询条件 + * @return 租户列表 + */ + List getTenantList(TenantExportReqVO exportReqVO); + + /** + * 获得名字对应的租户 + * + * @param name 组户名 + * @return 租户 + */ + TenantDO getTenantByName(String name); + + /** + * 获得使用指定套餐的租户数量 + * + * @param packageId 租户套餐编号 + * @return 租户数量 + */ + Long getTenantCountByPackageId(Long packageId); + + /** + * 获得使用指定套餐的租户数组 + * + * @param packageId 租户套餐编号 + * @return 租户数组 + */ + List getTenantListByPackageId(Long packageId); + + /** + * 进行租户的信息处理逻辑 + * 其中,租户编号从 {@link TenantContextHolder} 上下文中获取 + * + * @param handler 处理器 + */ + void handleTenantInfo(TenantInfoHandler handler); + + /** + * 进行租户的菜单处理逻辑 + * 其中,租户编号从 {@link TenantContextHolder} 上下文中获取 + * + * @param handler 处理器 + */ + void handleTenantMenu(TenantMenuHandler handler); + + /** + * 获得所有租户 + * + * @return 租户编号数组 + */ + List getTenantIds(); + + /** + * 校验租户是否合法 + * + * @param id 租户编号 + */ + void validTenant(Long id); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/handler/TenantInfoHandler.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/handler/TenantInfoHandler.java new file mode 100644 index 00000000..78574c59 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/handler/TenantInfoHandler.java @@ -0,0 +1,22 @@ +package com.ruoyi.system.service.handler; + + +import com.ruoyi.system.domain.TenantDO; + +/** + * 租户信息处理 + * 目的:尽量减少租户逻辑耦合到系统中 + * + * @author 芋道源码 + */ +public interface TenantInfoHandler { + + /** + * 基于传入的租户信息,进行相关逻辑的执行 + * 例如说,创建用户时,超过最大账户配额 + * + * @param tenant 租户信息 + */ + void handle(TenantDO tenant); + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/handler/TenantMenuHandler.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/handler/TenantMenuHandler.java new file mode 100644 index 00000000..e1831c95 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/handler/TenantMenuHandler.java @@ -0,0 +1,21 @@ +package com.ruoyi.system.service.handler; + +import java.util.Set; + +/** + * 租户菜单处理 + * 目的:尽量减少租户逻辑耦合到系统中 + * + * @author 芋道源码 + */ +public interface TenantMenuHandler { + + /** + * 基于传入的租户菜单【全】列表,进行相关逻辑的执行 + * 例如说,返回可分配菜单的时候,可以移除多余的 + * + * @param menuIds 菜单列表 + */ + void handle(Set menuIds); + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java new file mode 100644 index 00000000..62549f90 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java @@ -0,0 +1,235 @@ +package com.ruoyi.system.service.impl; + +import java.util.Collection; +import java.util.List; +import javax.annotation.PostConstruct; + +import com.ruoyi.common.utils.EhcacheUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.annotation.DataSource; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.enums.DataSourceType; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.domain.SysConfig; +import com.ruoyi.system.mapper.SysConfigMapper; +import com.ruoyi.system.service.ISysConfigService; + +/** + * 参数配置 服务层实现 + * + * @author ruoyi + */ +@Service +public class SysConfigServiceImpl implements ISysConfigService +{ + @Autowired + private SysConfigMapper configMapper; + +// @Autowired +// private RedisCache redisCache; + + /** + * 项目启动时,初始化参数到缓存 + */ + @PostConstruct + public void init() + { + loadingConfigCache(); + } + + /** + * 查询参数配置信息 + * + * @param configId 参数配置ID + * @return 参数配置信息 + */ + @Override + @DataSource(DataSourceType.MASTER) + public SysConfig selectConfigById(Long configId) + { + SysConfig config = new SysConfig(); + config.setConfigId(configId); + return configMapper.selectConfig(config); + } + + /** + * 根据键名查询参数配置信息 + * + * @param configKey 参数key + * @return 参数键值 + */ + @Override + public String selectConfigByKey(String configKey) + { +// String configValue = Convert.toStr(redisCache.getCacheObject(getCacheKey(configKey))); + Object obj = EhcacheUtil.get("sysconfig", getCacheKey(configKey)); + String configValue = Convert.toStr(obj); + if (StringUtils.isNotEmpty(configValue)) + { + return configValue; + } + SysConfig config = new SysConfig(); + config.setConfigKey(configKey); + SysConfig retConfig = configMapper.selectConfig(config); + if (StringUtils.isNotNull(retConfig)) + { + EhcacheUtil.put("sysconfig", getCacheKey(configKey),retConfig.getConfigValue()); +// redisCache.setCacheObject(getCacheKey(configKey), retConfig.getConfigValue()); + return retConfig.getConfigValue(); + } + return StringUtils.EMPTY; + } + + /** + * 获取验证码开关 + * + * @return true开启,false关闭 + */ + @Override + public boolean selectCaptchaEnabled() + { + String captchaEnabled = selectConfigByKey("sys.account.captchaEnabled"); + if (StringUtils.isEmpty(captchaEnabled)) + { + return true; + } + return Convert.toBool(captchaEnabled); + } + + /** + * 查询参数配置列表 + * + * @param config 参数配置信息 + * @return 参数配置集合 + */ + @Override + public List selectConfigList(SysConfig config) + { + return configMapper.selectConfigList(config); + } + + /** + * 新增参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + @Override + public int insertConfig(SysConfig config) + { + int row = configMapper.insertConfig(config); + if (row > 0) + { + EhcacheUtil.put("sysconfig", getCacheKey(config.getConfigKey()), config.getConfigValue()); +// redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue()); + } + return row; + } + + /** + * 修改参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + @Override + public int updateConfig(SysConfig config) + { + int row = configMapper.updateConfig(config); + if (row > 0) + { + EhcacheUtil.put("sysconfig", getCacheKey(config.getConfigKey()), config.getConfigValue()); +// redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue()); + } + return row; + } + + /** + * 批量删除参数信息 + * + * @param configIds 需要删除的参数ID + */ + @Override + public void deleteConfigByIds(Long[] configIds) + { + for (Long configId : configIds) + { + SysConfig config = selectConfigById(configId); + if (StringUtils.equals(UserConstants.YES, config.getConfigType())) + { + throw new ServiceException(String.format("内置参数【%1$s】不能删除 ", config.getConfigKey())); + } + configMapper.deleteConfigById(configId); + EhcacheUtil.remove("sysconfig", getCacheKey(config.getConfigKey())); +// redisCache.deleteObject(getCacheKey(config.getConfigKey())); + } + } + + /** + * 加载参数缓存数据 + */ + @Override + public void loadingConfigCache() + { + List configsList = configMapper.selectConfigList(new SysConfig()); + for (SysConfig config : configsList) + { + EhcacheUtil.put("sysconfig", getCacheKey(config.getConfigKey()),config.getConfigValue()); +// redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue()); + } + } + + /** + * 清空参数缓存数据 + */ + @Override + public void clearConfigCache() + { + EhcacheUtil.removeAll("sysconfig"); +// Collection keys = redisCache.keys(CacheConstants.SYS_CONFIG_KEY + "*"); +// redisCache.deleteObject(keys); + } + + /** + * 重置参数缓存数据 + */ + @Override + public void resetConfigCache() + { + clearConfigCache(); + loadingConfigCache(); + } + + /** + * 校验参数键名是否唯一 + * + * @param config 参数配置信息 + * @return 结果 + */ + @Override + public String checkConfigKeyUnique(SysConfig config) + { + Long configId = StringUtils.isNull(config.getConfigId()) ? -1L : config.getConfigId(); + SysConfig info = configMapper.checkConfigKeyUnique(config.getConfigKey()); + if (StringUtils.isNotNull(info) && info.getConfigId().longValue() != configId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 设置cache key + * + * @param configKey 参数键 + * @return 缓存键key + */ + private String getCacheKey(String configKey) + { + return CacheConstants.SYS_CONFIG_KEY + configKey; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java new file mode 100644 index 00000000..a9383287 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java @@ -0,0 +1,344 @@ +package com.ruoyi.system.service.impl; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.annotation.DataScope; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.TreeSelect; +import com.ruoyi.common.core.domain.entity.SysDept; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.system.mapper.SysDeptMapper; +import com.ruoyi.system.mapper.SysRoleMapper; +import com.ruoyi.system.service.ISysDeptService; + +/** + * 部门管理 服务实现 + * + * @author ruoyi + */ +@Service +public class SysDeptServiceImpl extends ServiceImpl implements ISysDeptService +{ + @Autowired + private SysDeptMapper deptMapper; + + @Autowired + private SysRoleMapper roleMapper; + + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * @return 部门信息集合 + */ + @Override + @DataScope(deptAlias = "d") + public List selectDeptList(SysDept dept) + { + return deptMapper.selectDeptList(dept); + } + + /** + * 查询部门树结构信息 + * + * @param dept 部门信息 + * @return 部门树信息集合 + */ + @Override + public List selectDeptTreeList(SysDept dept) + { + List depts = SpringUtils.getAopProxy(this).selectDeptList(dept); + return buildDeptTreeSelect(depts); + } + + /** + * 构建前端所需要树结构 + * + * @param depts 部门列表 + * @return 树结构列表 + */ + @Override + public List buildDeptTree(List depts) + { + List returnList = new ArrayList(); + List tempList = new ArrayList(); + for (SysDept dept : depts) + { + tempList.add(dept.getDeptId()); + } + for (SysDept dept : depts) + { + // 如果是顶级节点, 遍历该父节点的所有子节点 + if (!tempList.contains(dept.getParentId())) + { + recursionFn(depts, dept); + returnList.add(dept); + } + } + if (returnList.isEmpty()) + { + returnList = depts; + } + return returnList; + } + + /** + * 构建前端所需要下拉树结构 + * + * @param depts 部门列表 + * @return 下拉树结构列表 + */ + @Override + public List buildDeptTreeSelect(List depts) + { + List deptTrees = buildDeptTree(depts); + return deptTrees.stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @return 选中部门列表 + */ + @Override + public List selectDeptListByRoleId(Long roleId) + { + SysRole role = roleMapper.selectRoleById(roleId); + return deptMapper.selectDeptListByRoleId(roleId, role.isDeptCheckStrictly()); + } + + /** + * 根据部门ID查询信息 + * + * @param deptId 部门ID + * @return 部门信息 + */ + @Override + public SysDept selectDeptById(Long deptId) + { + return deptMapper.selectDeptById(deptId); + } + + /** + * 根据ID查询所有子部门(正常状态) + * + * @param deptId 部门ID + * @return 子部门数 + */ + @Override + public int selectNormalChildrenDeptById(Long deptId) + { + return deptMapper.selectNormalChildrenDeptById(deptId); + } + + /** + * 是否存在子节点 + * + * @param deptId 部门ID + * @return 结果 + */ + @Override + public boolean hasChildByDeptId(Long deptId) + { + int result = deptMapper.hasChildByDeptId(deptId); + return result > 0; + } + + /** + * 查询部门是否存在用户 + * + * @param deptId 部门ID + * @return 结果 true 存在 false 不存在 + */ + @Override + public boolean checkDeptExistUser(Long deptId) + { + int result = deptMapper.checkDeptExistUser(deptId); + return result > 0; + } + + /** + * 校验部门名称是否唯一 + * + * @param dept 部门信息 + * @return 结果 + */ + @Override + public String checkDeptNameUnique(SysDept dept) + { + Long deptId = StringUtils.isNull(dept.getDeptId()) ? -1L : dept.getDeptId(); + SysDept info = deptMapper.checkDeptNameUnique(dept.getDeptName(), dept.getParentId()); + if (StringUtils.isNotNull(info) && info.getDeptId().longValue() != deptId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验部门是否有数据权限 + * + * @param deptId 部门id + */ + @Override + public void checkDeptDataScope(Long deptId) + { + if (!SysUser.isAdmin(SecurityUtils.getUserId())) + { + SysDept dept = new SysDept(); + dept.setDeptId(deptId); + List depts = SpringUtils.getAopProxy(this).selectDeptList(dept); + if (StringUtils.isEmpty(depts)) + { + throw new ServiceException("没有权限访问部门数据!"); + } + } + } + + /** + * 新增保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + @Override + public int insertDept(SysDept dept) + { + SysDept info = deptMapper.selectDeptById(dept.getParentId()); + // 如果父节点不为正常状态,则不允许新增子节点 + if (!UserConstants.DEPT_NORMAL.equals(info.getStatus())) + { + throw new ServiceException("部门停用,不允许新增"); + } + dept.setAncestors(info.getAncestors() + "," + dept.getParentId()); + return deptMapper.insertDept(dept); + } + + /** + * 修改保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + @Override + public int updateDept(SysDept dept) + { + SysDept newParentDept = deptMapper.selectDeptById(dept.getParentId()); + SysDept oldDept = deptMapper.selectDeptById(dept.getDeptId()); + if (StringUtils.isNotNull(newParentDept) && StringUtils.isNotNull(oldDept)) + { + String newAncestors = newParentDept.getAncestors() + "," + newParentDept.getDeptId(); + String oldAncestors = oldDept.getAncestors(); + dept.setAncestors(newAncestors); + updateDeptChildren(dept.getDeptId(), newAncestors, oldAncestors); + } + int result = deptMapper.updateDept(dept); + if (UserConstants.DEPT_NORMAL.equals(dept.getStatus()) && StringUtils.isNotEmpty(dept.getAncestors()) + && !StringUtils.equals("0", dept.getAncestors())) + { + // 如果该部门是启用状态,则启用该部门的所有上级部门 + updateParentDeptStatusNormal(dept); + } + return result; + } + + /** + * 修改该部门的父级部门状态 + * + * @param dept 当前部门 + */ + private void updateParentDeptStatusNormal(SysDept dept) + { + String ancestors = dept.getAncestors(); + Long[] deptIds = Convert.toLongArray(ancestors); + deptMapper.updateDeptStatusNormal(deptIds); + } + + /** + * 修改子元素关系 + * + * @param deptId 被修改的部门ID + * @param newAncestors 新的父ID集合 + * @param oldAncestors 旧的父ID集合 + */ + public void updateDeptChildren(Long deptId, String newAncestors, String oldAncestors) + { + List children = deptMapper.selectChildrenDeptById(deptId); + for (SysDept child : children) + { + child.setAncestors(child.getAncestors().replaceFirst(oldAncestors, newAncestors)); + } + if (children.size() > 0) + { + deptMapper.updateDeptChildren(children); + } + } + + /** + * 删除部门管理信息 + * + * @param deptId 部门ID + * @return 结果 + */ + @Override + public int deleteDeptById(Long deptId) + { + return deptMapper.deleteDeptById(deptId); + } + + /** + * 递归列表 + */ + private void recursionFn(List list, SysDept t) + { + // 得到子节点列表 + List childList = getChildList(list, t); + t.setChildren(childList); + for (SysDept tChild : childList) + { + if (hasChild(list, tChild)) + { + recursionFn(list, tChild); + } + } + } + + /** + * 得到子节点列表 + */ + private List getChildList(List list, SysDept t) + { + List tlist = new ArrayList(); + Iterator it = list.iterator(); + while (it.hasNext()) + { + SysDept n = (SysDept) it.next(); + if (StringUtils.isNotNull(n.getParentId()) && n.getParentId().longValue() == t.getDeptId().longValue()) + { + tlist.add(n); + } + } + return tlist; + } + + /** + * 判断是否有子节点 + */ + private boolean hasChild(List list, SysDept t) + { + return getChildList(list, t).size() > 0; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java new file mode 100644 index 00000000..40c1d673 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java @@ -0,0 +1,113 @@ +package com.ruoyi.system.service.impl; + +import java.util.List; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.core.domain.entity.SysDictData; +import com.ruoyi.common.utils.DictUtils; +import com.ruoyi.system.mapper.SysDictDataMapper; +import com.ruoyi.system.service.ISysDictDataService; + +/** + * 字典 业务层处理 + * + * @author ruoyi + */ +@Service +public class SysDictDataServiceImpl extends ServiceImpl implements ISysDictDataService +{ + @Autowired + private SysDictDataMapper dictDataMapper; + + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * @return 字典数据集合信息 + */ + @Override + public List selectDictDataList(SysDictData dictData) + { + return dictDataMapper.selectDictDataList(dictData); + } + + /** + * 根据字典类型和字典键值查询字典数据信息 + * + * @param dictType 字典类型 + * @param dictValue 字典键值 + * @return 字典标签 + */ + @Override + public String selectDictLabel(String dictType, String dictValue) + { + return dictDataMapper.selectDictLabel(dictType, dictValue); + } + + /** + * 根据字典数据ID查询信息 + * + * @param dictCode 字典数据ID + * @return 字典数据 + */ + @Override + public SysDictData selectDictDataById(Long dictCode) + { + return dictDataMapper.selectDictDataById(dictCode); + } + + /** + * 批量删除字典数据信息 + * + * @param dictCodes 需要删除的字典数据ID + */ + @Override + public void deleteDictDataByIds(Long[] dictCodes) + { + for (Long dictCode : dictCodes) + { + SysDictData data = selectDictDataById(dictCode); + dictDataMapper.deleteDictDataById(dictCode); + List dictDatas = dictDataMapper.selectDictDataByType(data.getDictType()); + DictUtils.setDictCache(data.getDictType(), dictDatas); + } + } + + /** + * 新增保存字典数据信息 + * + * @param data 字典数据信息 + * @return 结果 + */ + @Override + public int insertDictData(SysDictData data) + { + int row = dictDataMapper.insertDictData(data); + if (row > 0) + { + List dictDatas = dictDataMapper.selectDictDataByType(data.getDictType()); + DictUtils.setDictCache(data.getDictType(), dictDatas); + } + return row; + } + + /** + * 修改保存字典数据信息 + * + * @param data 字典数据信息 + * @return 结果 + */ + @Override + public int updateDictData(SysDictData data) + { + int row = dictDataMapper.updateDictData(data); + if (row > 0) + { + List dictDatas = dictDataMapper.selectDictDataByType(data.getDictType()); + DictUtils.setDictCache(data.getDictType(), dictDatas); + } + return row; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java new file mode 100644 index 00000000..e8f0b1b9 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java @@ -0,0 +1,225 @@ +package com.ruoyi.system.service.impl; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import javax.annotation.PostConstruct; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.entity.SysDictData; +import com.ruoyi.common.core.domain.entity.SysDictType; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.DictUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.mapper.SysDictDataMapper; +import com.ruoyi.system.mapper.SysDictTypeMapper; +import com.ruoyi.system.service.ISysDictTypeService; + +/** + * 字典 业务层处理 + * + * @author ruoyi + */ +@Service +public class SysDictTypeServiceImpl extends ServiceImpl implements ISysDictTypeService +{ + @Autowired + private SysDictTypeMapper dictTypeMapper; + + @Autowired + private SysDictDataMapper dictDataMapper; + + /** + * 项目启动时,初始化字典到缓存 + */ + @PostConstruct + public void init() + { + loadingDictCache(); + } + + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * @return 字典类型集合信息 + */ + @Override + public List selectDictTypeList(SysDictType dictType) + { + return dictTypeMapper.selectDictTypeList(dictType); + } + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + @Override + public List selectDictTypeAll() + { + return dictTypeMapper.selectDictTypeAll(); + } + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据集合信息 + */ + @Override + public List selectDictDataByType(String dictType) + { + List dictDatas = DictUtils.getDictCache(dictType); + if (StringUtils.isNotEmpty(dictDatas)) + { + return dictDatas; + } + dictDatas = dictDataMapper.selectDictDataByType(dictType); + if (StringUtils.isNotEmpty(dictDatas)) + { + DictUtils.setDictCache(dictType, dictDatas); + return dictDatas; + } + return null; + } + + /** + * 根据字典类型ID查询信息 + * + * @param dictId 字典类型ID + * @return 字典类型 + */ + @Override + public SysDictType selectDictTypeById(Long dictId) + { + return dictTypeMapper.selectDictTypeById(dictId); + } + + /** + * 根据字典类型查询信息 + * + * @param dictType 字典类型 + * @return 字典类型 + */ + @Override + public SysDictType selectDictTypeByType(String dictType) + { + return dictTypeMapper.selectDictTypeByType(dictType); + } + + /** + * 批量删除字典类型信息 + * + * @param dictIds 需要删除的字典ID + */ + @Override + public void deleteDictTypeByIds(Long[] dictIds) + { + for (Long dictId : dictIds) + { + SysDictType dictType = selectDictTypeById(dictId); + if (dictDataMapper.countDictDataByType(dictType.getDictType()) > 0) + { + throw new ServiceException(String.format("%1$s已分配,不能删除", dictType.getDictName())); + } + dictTypeMapper.deleteDictTypeById(dictId); + DictUtils.removeDictCache(dictType.getDictType()); + } + } + + /** + * 加载字典缓存数据 + */ + @Override + public void loadingDictCache() + { + SysDictData dictData = new SysDictData(); + dictData.setStatus("0"); + Map> dictDataMap = dictDataMapper.selectDictDataList(dictData).stream().collect(Collectors.groupingBy(SysDictData::getDictType)); + for (Map.Entry> entry : dictDataMap.entrySet()) + { + DictUtils.setDictCache(entry.getKey(), entry.getValue().stream().sorted(Comparator.comparing(SysDictData::getDictSort)).collect(Collectors.toList())); + } + } + + /** + * 清空字典缓存数据 + */ + @Override + public void clearDictCache() + { + DictUtils.clearDictCache(); + } + + /** + * 重置字典缓存数据 + */ + @Override + public void resetDictCache() + { + clearDictCache(); + loadingDictCache(); + } + + /** + * 新增保存字典类型信息 + * + * @param dict 字典类型信息 + * @return 结果 + */ + @Override + public int insertDictType(SysDictType dict) + { + int row = dictTypeMapper.insertDictType(dict); + if (row > 0) + { + DictUtils.setDictCache(dict.getDictType(), null); + } + return row; + } + + /** + * 修改保存字典类型信息 + * + * @param dict 字典类型信息 + * @return 结果 + */ + @Override + @Transactional + public int updateDictType(SysDictType dict) + { + SysDictType oldDict = dictTypeMapper.selectDictTypeById(dict.getDictId()); + dictDataMapper.updateDictDataType(oldDict.getDictType(), dict.getDictType()); + int row = dictTypeMapper.updateDictType(dict); + if (row > 0) + { + List dictDatas = dictDataMapper.selectDictDataByType(dict.getDictType()); + DictUtils.setDictCache(dict.getDictType(), dictDatas); + } + return row; + } + + /** + * 校验字典类型称是否唯一 + * + * @param dict 字典类型 + * @return 结果 + */ + @Override + public String checkDictTypeUnique(SysDictType dict) + { + Long dictId = StringUtils.isNull(dict.getDictId()) ? -1L : dict.getDictId(); + SysDictType dictType = dictTypeMapper.checkDictTypeUnique(dict.getDictType()); + if (StringUtils.isNotNull(dictType) && dictType.getDictId().longValue() != dictId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java new file mode 100644 index 00000000..f013d18a --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java @@ -0,0 +1,68 @@ +package com.ruoyi.system.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.system.domain.SysLogininfor; +import com.ruoyi.system.mapper.SysLogininforMapper; +import com.ruoyi.system.service.ISysLogininforService; + +/** + * 系统访问日志情况信息 服务层处理 + * + * @author ruoyi + */ +@Service +public class SysLogininforServiceImpl implements ISysLogininforService +{ + + @Autowired + private SysLogininforMapper logininforMapper; + + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + @Override + public void insertLogininfor(SysLogininfor logininfor) + { + if (logininfor.getMsg() != null && logininfor.getMsg().length() >255) { + logininfor.setMsg(logininfor.getMsg().substring(0,250)); + } + logininforMapper.insertLogininfor(logininfor); + } + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * @return 登录记录集合 + */ + @Override + public List selectLogininforList(SysLogininfor logininfor) + { + return logininforMapper.selectLogininforList(logininfor); + } + + /** + * 批量删除系统登录日志 + * + * @param infoIds 需要删除的登录日志ID + * @return 结果 + */ + @Override + public int deleteLogininforByIds(Long[] infoIds) + { + return logininforMapper.deleteLogininforByIds(infoIds); + } + + /** + * 清空系统登录日志 + */ + @Override + public void cleanLogininfor() + { + logininforMapper.cleanLogininfor(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java new file mode 100644 index 00000000..58d2b0c2 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java @@ -0,0 +1,562 @@ +package com.ruoyi.system.service.impl; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.system.service.TenantService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.TreeSelect; +import com.ruoyi.common.core.domain.entity.SysMenu; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.domain.vo.MetaVo; +import com.ruoyi.system.domain.vo.RouterVo; +import com.ruoyi.system.mapper.SysMenuMapper; +import com.ruoyi.system.mapper.SysRoleMapper; +import com.ruoyi.system.mapper.SysRoleMenuMapper; +import com.ruoyi.system.service.ISysMenuService; + +import javax.annotation.Resource; + +/** + * 菜单 业务层处理 + * + * @author ruoyi + */ +@Service +public class SysMenuServiceImpl extends ServiceImpl implements ISysMenuService +{ + public static final String PREMISSION_STRING = "perms[\"{0}\"]"; + + @Autowired + private SysMenuMapper menuMapper; + + @Autowired + private SysRoleMapper roleMapper; + + @Autowired + private SysRoleMenuMapper roleMenuMapper; + @Resource + @Lazy // 延迟,避免循环依赖报错 + private TenantService tenantService; + /** + * 根据用户查询系统菜单列表 + * + * @param userId 用户ID + * @return 菜单列表 + */ + @Override + public List selectMenuList(Long userId) + { + return selectMenuList(new SysMenu(), userId); + } + + /** + * 查询系统菜单列表 + * + * @param menu 菜单信息 + * @return 菜单列表 + */ + @Override + public List selectMenuList(SysMenu menu, Long userId) + { + List menuList = null; + // 管理员显示所有菜单信息 + if (SysUser.isAdmin(userId)) + { + menuList = menuMapper.selectMenuList(menu); + } + else + { + menu.getParams().put("userId", userId); + menuList = menuMapper.selectMenuListByUserId(menu); + } + return menuList; + } + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + @Override + public Set selectMenuPermsByUserId(Long userId) + { + List perms = menuMapper.selectMenuPermsByUserId(userId); + Set permsSet = new HashSet<>(); + for (String perm : perms) + { + if (StringUtils.isNotEmpty(perm)) + { + permsSet.addAll(Arrays.asList(perm.trim().split(","))); + } + } + return permsSet; + } + + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * @return 权限列表 + */ + @Override + public Set selectMenuPermsByRoleId(Long roleId) + { + List perms = menuMapper.selectMenuPermsByRoleId(roleId); + Set permsSet = new HashSet<>(); + for (String perm : perms) + { + if (StringUtils.isNotEmpty(perm)) + { + permsSet.addAll(Arrays.asList(perm.trim().split(","))); + } + } + return permsSet; + } + + /** + * 根据用户ID查询菜单 + * + * @param userId 用户名称 + * @return 菜单列表 + */ + @Override + public List selectMenuTreeByUserId(Long userId) + { + List menus = null; + if (SecurityUtils.isAdmin(userId)) + { + menus = menuMapper.selectMenuTreeAll(); + } + else + { + menus = menuMapper.selectMenuTreeByUserId(userId); + } + return getChildPerms(menus, 0); + } + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @return 选中菜单列表 + */ + @Override + public List selectMenuListByRoleId(Long roleId) + { + SysRole role = roleMapper.selectRoleById(roleId); + return menuMapper.selectMenuListByRoleId(roleId, role.isMenuCheckStrictly()); + } + + /** + * 构建前端路由所需要的菜单 + * + * @param menus 菜单列表 + * @return 路由列表 + */ + @Override + public List buildMenus(List menus) + { + List routers = new LinkedList(); + for (SysMenu menu : menus) + { + RouterVo router = new RouterVo(); + router.setHidden("1".equals(menu.getVisible())); + router.setName(getRouteName(menu)); + router.setPath(getRouterPath(menu)); + router.setComponent(getComponent(menu)); + router.setQuery(menu.getQuery()); + router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath())); + List cMenus = menu.getChildren(); + if (!cMenus.isEmpty() && cMenus.size() > 0 && UserConstants.TYPE_DIR.equals(menu.getMenuType())) + { + router.setAlwaysShow(true); + router.setRedirect("noRedirect"); + router.setChildren(buildMenus(cMenus)); + } + else if (isMenuFrame(menu)) + { + router.setMeta(null); + List childrenList = new ArrayList(); + RouterVo children = new RouterVo(); + children.setPath(menu.getPath()); + children.setComponent(menu.getComponent()); + children.setName(StringUtils.capitalize(menu.getPath())); + children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath())); + children.setQuery(menu.getQuery()); + childrenList.add(children); + router.setChildren(childrenList); + } + else if (menu.getParentId().intValue() == 0 && isInnerLink(menu)) + { + router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon())); + router.setPath("/"); + List childrenList = new ArrayList(); + RouterVo children = new RouterVo(); + String routerPath = innerLinkReplaceEach(menu.getPath()); + children.setPath(routerPath); + children.setComponent(UserConstants.INNER_LINK); + children.setName(StringUtils.capitalize(routerPath)); + children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), menu.getPath())); + childrenList.add(children); + router.setChildren(childrenList); + } + routers.add(router); + } + return routers; + } + + /** + * 构建前端所需要树结构 + * + * @param menus 菜单列表 + * @return 树结构列表 + */ + @Override + public List buildMenuTree(List menus) + { + List returnList = new ArrayList(); + List tempList = new ArrayList(); + for (SysMenu dept : menus) + { + tempList.add(dept.getMenuId()); + } + for (Iterator iterator = menus.iterator(); iterator.hasNext();) + { + SysMenu menu = (SysMenu) iterator.next(); + // 如果是顶级节点, 遍历该父节点的所有子节点 + if (!tempList.contains(menu.getParentId())) + { + recursionFn(menus, menu); + returnList.add(menu); + } + } + if (returnList.isEmpty()) + { + returnList = menus; + } + return returnList; + } + + /** + * 构建前端所需要下拉树结构 + * + * @param menus 菜单列表 + * @return 下拉树结构列表 + */ + @Override + public List buildMenuTreeSelect(List menus) + { + List menuTrees = buildMenuTree(menus); + return menuTrees.stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + /** + * 根据菜单ID查询信息 + * + * @param menuId 菜单ID + * @return 菜单信息 + */ + @Override + public SysMenu selectMenuById(Long menuId) + { + return menuMapper.selectMenuById(menuId); + } + + /** + * 是否存在菜单子节点 + * + * @param menuId 菜单ID + * @return 结果 + */ + @Override + public boolean hasChildByMenuId(Long menuId) + { + int result = menuMapper.hasChildByMenuId(menuId); + return result > 0; + } + + /** + * 查询菜单使用数量 + * + * @param menuId 菜单ID + * @return 结果 + */ + @Override + public boolean checkMenuExistRole(Long menuId) + { + int result = roleMenuMapper.checkMenuExistRole(menuId); + return result > 0; + } + + /** + * 新增保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + @Override + public int insertMenu(SysMenu menu) + { + return menuMapper.insertMenu(menu); + } + + /** + * 修改保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + @Override + public int updateMenu(SysMenu menu) + { + return menuMapper.updateMenu(menu); + } + + /** + * 删除菜单管理信息 + * + * @param menuId 菜单ID + * @return 结果 + */ + @Override + public int deleteMenuById(Long menuId) + { + return menuMapper.deleteMenuById(menuId); + } + + /** + * 校验菜单名称是否唯一 + * + * @param menu 菜单信息 + * @return 结果 + */ + @Override + public String checkMenuNameUnique(SysMenu menu) + { + Long menuId = StringUtils.isNull(menu.getMenuId()) ? -1L : menu.getMenuId(); + SysMenu info = menuMapper.checkMenuNameUnique(menu.getMenuName(), menu.getParentId()); + if (StringUtils.isNotNull(info) && info.getMenuId().longValue() != menuId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 获取路由名称 + * + * @param menu 菜单信息 + * @return 路由名称 + */ + public String getRouteName(SysMenu menu) + { + String routerName = StringUtils.capitalize(menu.getPath()); + // 非外链并且是一级目录(类型为目录) + if (isMenuFrame(menu)) + { + routerName = StringUtils.EMPTY; + } + return routerName; + } + + /** + * 获取路由地址 + * + * @param menu 菜单信息 + * @return 路由地址 + */ + public String getRouterPath(SysMenu menu) + { + String routerPath = menu.getPath(); + // 内链打开外网方式 + if (menu.getParentId().intValue() != 0 && isInnerLink(menu)) + { + routerPath = innerLinkReplaceEach(routerPath); + } + // 非外链并且是一级目录(类型为目录) + if (0 == menu.getParentId().intValue() && UserConstants.TYPE_DIR.equals(menu.getMenuType()) + && UserConstants.NO_FRAME.equals(menu.getIsFrame())) + { + routerPath = "/" + menu.getPath(); + } + // 非外链并且是一级目录(类型为菜单) + else if (isMenuFrame(menu)) + { + routerPath = "/"; + } + return routerPath; + } + + /** + * 获取组件信息 + * + * @param menu 菜单信息 + * @return 组件信息 + */ + public String getComponent(SysMenu menu) + { + String component = UserConstants.LAYOUT; + if (StringUtils.isNotEmpty(menu.getComponent()) && !isMenuFrame(menu)) + { + component = menu.getComponent(); + } + else if (StringUtils.isEmpty(menu.getComponent()) && menu.getParentId().intValue() != 0 && isInnerLink(menu)) + { + component = UserConstants.INNER_LINK; + } + else if (StringUtils.isEmpty(menu.getComponent()) && isParentView(menu)) + { + component = UserConstants.PARENT_VIEW; + } + return component; + } + + /** + * 是否为菜单内部跳转 + * + * @param menu 菜单信息 + * @return 结果 + */ + public boolean isMenuFrame(SysMenu menu) + { + return menu.getParentId().intValue() == 0 && UserConstants.TYPE_MENU.equals(menu.getMenuType()) + && menu.getIsFrame().equals(UserConstants.NO_FRAME); + } + + /** + * 是否为内链组件 + * + * @param menu 菜单信息 + * @return 结果 + */ + public boolean isInnerLink(SysMenu menu) + { + return menu.getIsFrame().equals(UserConstants.NO_FRAME) && StringUtils.ishttp(menu.getPath()); + } + + /** + * 是否为parent_view组件 + * + * @param menu 菜单信息 + * @return 结果 + */ + public boolean isParentView(SysMenu menu) + { + return menu.getParentId().intValue() != 0 && UserConstants.TYPE_DIR.equals(menu.getMenuType()); + } + + /** + * 根据父节点的ID获取所有子节点 + * + * @param list 分类表 + * @param parentId 传入的父节点ID + * @return String + */ + public List getChildPerms(List list, int parentId) + { + List returnList = new ArrayList(); + for (Iterator iterator = list.iterator(); iterator.hasNext();) + { + SysMenu t = (SysMenu) iterator.next(); + // 一、根据传入的某个父节点ID,遍历该父节点的所有子节点 + if (t.getParentId() == parentId) + { + recursionFn(list, t); + returnList.add(t); + } + } + return returnList; + } + + /** + * 递归列表 + * + * @param list + * @param t + */ + private void recursionFn(List list, SysMenu t) + { + // 得到子节点列表 + List childList = getChildList(list, t); + t.setChildren(childList); + for (SysMenu tChild : childList) + { + if (hasChild(list, tChild)) + { + recursionFn(list, tChild); + } + } + } + + /** + * 得到子节点列表 + */ + private List getChildList(List list, SysMenu t) + { + List tlist = new ArrayList(); + Iterator it = list.iterator(); + while (it.hasNext()) + { + SysMenu n = (SysMenu) it.next(); + if (n.getParentId().longValue() == t.getMenuId().longValue()) + { + tlist.add(n); + } + } + return tlist; + } + + /** + * 判断是否有子节点 + */ + private boolean hasChild(List list, SysMenu t) + { + return getChildList(list, t).size() > 0; + } + + /** + * 内链域名特殊字符替换 + * + * @return + */ + public String innerLinkReplaceEach(String path) + { + return StringUtils.replaceEach(path, new String[] { Constants.HTTP, Constants.HTTPS, Constants.WWW, "." }, + new String[] { "", "", "", "/" }); + } + + @Override + public List getTenantMenus(SysMenu sysMenu) { + List menus = menuMapper.selectList(sysMenu); + Iterator iterator = menus.iterator(); +// while (iterator.hasNext()) { +// SysMenu next = iterator.next(); +// if ("C".equalsIgnoreCase(next.getMenuType()) || "F".equalsIgnoreCase(next.getMenuType())) { +// SysMenu pm = CollUtil.findOneByField(menus, "menuId", next.getParentId()); +// if (pm == null) { +// iterator.remove(); +// } +// } +// } + // 开启多租户的情况下,需要过滤掉未开通的菜单 + tenantService.handleTenantMenu(menuIds -> menus.removeIf(menu -> !CollUtil.contains(menuIds, menu.getMenuId()))); + return menus; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeServiceImpl.java new file mode 100644 index 00000000..765438b8 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeServiceImpl.java @@ -0,0 +1,92 @@ +package com.ruoyi.system.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.system.domain.SysNotice; +import com.ruoyi.system.mapper.SysNoticeMapper; +import com.ruoyi.system.service.ISysNoticeService; + +/** + * 公告 服务层实现 + * + * @author ruoyi + */ +@Service +public class SysNoticeServiceImpl implements ISysNoticeService +{ + @Autowired + private SysNoticeMapper noticeMapper; + + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * @return 公告信息 + */ + @Override + public SysNotice selectNoticeById(Long noticeId) + { + return noticeMapper.selectNoticeById(noticeId); + } + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * @return 公告集合 + */ + @Override + public List selectNoticeList(SysNotice notice) + { + return noticeMapper.selectNoticeList(notice); + } + + /** + * 新增公告 + * + * @param notice 公告信息 + * @return 结果 + */ + @Override + public int insertNotice(SysNotice notice) + { + return noticeMapper.insertNotice(notice); + } + + /** + * 修改公告 + * + * @param notice 公告信息 + * @return 结果 + */ + @Override + public int updateNotice(SysNotice notice) + { + return noticeMapper.updateNotice(notice); + } + + /** + * 删除公告对象 + * + * @param noticeId 公告ID + * @return 结果 + */ + @Override + public int deleteNoticeById(Long noticeId) + { + return noticeMapper.deleteNoticeById(noticeId); + } + + /** + * 批量删除公告信息 + * + * @param noticeIds 需要删除的公告ID + * @return 结果 + */ + @Override + public int deleteNoticeByIds(Long[] noticeIds) + { + return noticeMapper.deleteNoticeByIds(noticeIds); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java new file mode 100644 index 00000000..54898152 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java @@ -0,0 +1,76 @@ +package com.ruoyi.system.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.system.domain.SysOperLog; +import com.ruoyi.system.mapper.SysOperLogMapper; +import com.ruoyi.system.service.ISysOperLogService; + +/** + * 操作日志 服务层处理 + * + * @author ruoyi + */ +@Service +public class SysOperLogServiceImpl implements ISysOperLogService +{ + @Autowired + private SysOperLogMapper operLogMapper; + + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + */ + @Override + public void insertOperlog(SysOperLog operLog) + { + operLogMapper.insertOperlog(operLog); + } + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * @return 操作日志集合 + */ + @Override + public List selectOperLogList(SysOperLog operLog) + { + return operLogMapper.selectOperLogList(operLog); + } + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * @return 结果 + */ + @Override + public int deleteOperLogByIds(Long[] operIds) + { + return operLogMapper.deleteOperLogByIds(operIds); + } + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * @return 操作日志对象 + */ + @Override + public SysOperLog selectOperLogById(Long operId) + { + return operLogMapper.selectOperLogById(operId); + } + + /** + * 清空操作日志 + */ + @Override + public void cleanOperLog() + { + operLogMapper.cleanOperLog(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPostServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPostServiceImpl.java new file mode 100644 index 00000000..0e7ff371 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPostServiceImpl.java @@ -0,0 +1,180 @@ +package com.ruoyi.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.domain.SysPost; +import com.ruoyi.system.mapper.SysPostMapper; +import com.ruoyi.system.mapper.SysUserPostMapper; +import com.ruoyi.system.service.ISysPostService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 岗位信息 服务层处理 + * + * @author ruoyi + */ +@Service +public class SysPostServiceImpl extends ServiceImpl implements ISysPostService +{ + @Autowired + private SysPostMapper postMapper; + + @Autowired + private SysUserPostMapper userPostMapper; + + /** + * 查询岗位信息集合 + * + * @param post 岗位信息 + * @return 岗位信息集合 + */ + @Override + public List selectPostList(SysPost post) + { + return postMapper.selectPostList(post); + } + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + @Override + public List selectPostAll() + { + return postMapper.selectPostAll(); + } + + /** + * 通过岗位ID查询岗位信息 + * + * @param postId 岗位ID + * @return 角色对象信息 + */ + @Override + public SysPost selectPostById(Long postId) + { + return postMapper.selectPostById(postId); + } + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * @return 选中岗位ID列表 + */ + @Override + public List selectPostListByUserId(Long userId) + { + return postMapper.selectPostListByUserId(userId); + } + + /** + * 校验岗位名称是否唯一 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public String checkPostNameUnique(SysPost post) + { + Long postId = StringUtils.isNull(post.getPostId()) ? -1L : post.getPostId(); + SysPost info = postMapper.checkPostNameUnique(post.getPostName()); + if (StringUtils.isNotNull(info) && info.getPostId().longValue() != postId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验岗位编码是否唯一 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public String checkPostCodeUnique(SysPost post) + { + Long postId = StringUtils.isNull(post.getPostId()) ? -1L : post.getPostId(); + SysPost info = postMapper.checkPostCodeUnique(post.getPostCode()); + if (StringUtils.isNotNull(info) && info.getPostId().longValue() != postId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 通过岗位ID查询岗位使用数量 + * + * @param postId 岗位ID + * @return 结果 + */ + @Override + public int countUserPostById(Long postId) + { + return userPostMapper.countUserPostById(postId); + } + + /** + * 删除岗位信息 + * + * @param postId 岗位ID + * @return 结果 + */ + @Override + public int deletePostById(Long postId) + { + return postMapper.deletePostById(postId); + } + + /** + * 批量删除岗位信息 + * + * @param postIds 需要删除的岗位ID + * @return 结果 + */ + @Override + public int deletePostByIds(Long[] postIds) + { + for (Long postId : postIds) + { + SysPost post = selectPostById(postId); + if (countUserPostById(postId) > 0) + { + throw new ServiceException(String.format("%1$s已分配,不能删除", post.getPostName())); + } + } + return postMapper.deletePostByIds(postIds); + } + + /** + * 新增保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public int insertPost(SysPost post) + { + return postMapper.insertPost(post); + } + + /** + * 修改保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public int updatePost(SysPost post) + { + return postMapper.updatePost(post); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java new file mode 100644 index 00000000..230d723b --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java @@ -0,0 +1,409 @@ +package com.ruoyi.system.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.common.annotation.DataScope; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.entity.SysMenu; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.enums.RoleCodeEnum; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.mybatis.query.LambdaQueryWrapperX; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.system.domain.SysRoleDept; +import com.ruoyi.system.domain.SysRoleMenu; +import com.ruoyi.system.domain.SysUserRole; +import com.ruoyi.system.mapper.*; +import com.ruoyi.system.service.ISysRoleService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; + +import static com.ruoyi.common.utils.collection.CollectionUtils.convertSet; + +/** + * 角色 业务层处理 + * + * @author ruoyi + */ +@Service +public class SysRoleServiceImpl extends ServiceImpl implements ISysRoleService { + @Autowired + private SysRoleMapper roleMapper; + + @Autowired + private SysRoleMenuMapper roleMenuMapper; + + @Autowired + private SysUserRoleMapper userRoleMapper; + + @Autowired + private SysRoleDeptMapper roleDeptMapper; + @Autowired + private SysMenuMapper menuMapper; + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * @return 角色数据集合信息 + */ + @Override + @DataScope(deptAlias = "d") + public List selectRoleList(SysRole role) { + return roleMapper.selectRoleList(role); + } + + /** + * 根据用户ID查询角色 + * + * @param userId 用户ID + * @return 角色列表 + */ + @Override + public List selectRolesByUserId(Long userId) { + List userRoles = roleMapper.selectRolePermissionByUserId(userId); + List roles = selectRoleAll(); + for (SysRole role : roles) { + for (SysRole userRole : userRoles) { + if (role.getRoleId().longValue() == userRole.getRoleId().longValue()) { + role.setFlag(true); + break; + } + } + } + return roles; + } + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + @Override + public Set selectRolePermissionByUserId(Long userId) { + List perms = roleMapper.selectRolePermissionByUserId(userId); + Set permsSet = new HashSet<>(); + for (SysRole perm : perms) { + if (StringUtils.isNotNull(perm)) { + permsSet.addAll(Arrays.asList(perm.getRoleKey().trim().split(","))); + } + } + return permsSet; + } + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + @Override + public List selectRoleAll() { + return SpringUtils.getAopProxy(this).selectRoleList(new SysRole()); + } + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + @Override + public List selectRoleListByUserId(Long userId) { + return roleMapper.selectRoleListByUserId(userId); + } + + /** + * 通过角色ID查询角色 + * + * @param roleId 角色ID + * @return 角色对象信息 + */ + @Override + public SysRole selectRoleById(Long roleId) { + return roleMapper.selectRoleById(roleId); + } + + /** + * 校验角色名称是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + public String checkRoleNameUnique(SysRole role) { + Long roleId = StringUtils.isNull(role.getRoleId()) ? -1L : role.getRoleId(); + SysRole info = roleMapper.checkRoleNameUnique(role.getRoleName()); + if (StringUtils.isNotNull(info) && info.getRoleId().longValue() != roleId.longValue()) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验角色权限是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + public String checkRoleKeyUnique(SysRole role) { + Long roleId = StringUtils.isNull(role.getRoleId()) ? -1L : role.getRoleId(); + SysRole info = roleMapper.checkRoleKeyUnique(role.getRoleKey()); + if (StringUtils.isNotNull(info) && info.getRoleId().longValue() != roleId.longValue()) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验角色是否允许操作 + * + * @param role 角色信息 + */ + @Override + public void checkRoleAllowed(SysRole role) { + if (StringUtils.isNotNull(role.getRoleId()) && role.isAdmin()) { + throw new ServiceException("不允许操作超级管理员角色"); + } + } + + /** + * 校验角色是否有数据权限 + * + * @param roleId 角色id + */ + @Override + public void checkRoleDataScope(Long roleId) { + if (!SysUser.isAdmin(SecurityUtils.getUserId())) { + SysRole role = new SysRole(); + role.setRoleId(roleId); + List roles = SpringUtils.getAopProxy(this).selectRoleList(role); + if (StringUtils.isEmpty(roles)) { + throw new ServiceException("没有权限访问角色数据!"); + } + } + } + + /** + * 通过角色ID查询角色使用数量 + * + * @param roleId 角色ID + * @return 结果 + */ + @Override + public int countUserRoleByRoleId(Long roleId) { + return userRoleMapper.countUserRoleByRoleId(roleId); + } + + /** + * 新增保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + @Transactional + public int insertRole(SysRole role) { + // 新增角色信息 + roleMapper.insertRole(role); + return insertRoleMenu(role); + } + + /** + * 修改保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + @Transactional + public int updateRole(SysRole role) { + // 修改角色信息 + roleMapper.updateRole(role); + // 删除角色与菜单关联 + roleMenuMapper.deleteRoleMenuByRoleId(role.getRoleId()); + return insertRoleMenu(role); + } + + /** + * 修改角色状态 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + public int updateRoleStatus(SysRole role) { + return roleMapper.updateRole(role); + } + + /** + * 修改数据权限信息 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + @Transactional + public int authDataScope(SysRole role) { + // 修改角色信息 + roleMapper.updateRole(role); + // 删除角色与部门关联 + roleDeptMapper.deleteRoleDeptByRoleId(role.getRoleId()); + // 新增角色和部门信息(数据权限) + return insertRoleDept(role); + } + + /** + * 新增角色菜单信息 + * + * @param role 角色对象 + */ + public int insertRoleMenu(SysRole role) { + int rows = 1; + // 新增用户与角色管理 + List list = new ArrayList(); + for (Long menuId : role.getMenuIds()) { + SysRoleMenu rm = new SysRoleMenu(); + rm.setRoleId(role.getRoleId()); + rm.setMenuId(menuId); + list.add(rm); + } + if (list.size() > 0) { + rows = roleMenuMapper.batchRoleMenu(list); + } + return rows; + } + + /** + * 新增角色部门信息(数据权限) + * + * @param role 角色对象 + */ + public int insertRoleDept(SysRole role) { + int rows = 1; + // 新增角色与部门(数据权限)管理 + List list = new ArrayList(); + for (Long deptId : role.getDeptIds()) { + SysRoleDept rd = new SysRoleDept(); + rd.setRoleId(role.getRoleId()); + rd.setDeptId(deptId); + list.add(rd); + } + if (list.size() > 0) { + rows = roleDeptMapper.batchRoleDept(list); + } + return rows; + } + + /** + * 通过角色ID删除角色 + * + * @param roleId 角色ID + * @return 结果 + */ + @Override + @Transactional + public int deleteRoleById(Long roleId) { + // 删除角色与菜单关联 + roleMenuMapper.deleteRoleMenuByRoleId(roleId); + // 删除角色与部门关联 + roleDeptMapper.deleteRoleDeptByRoleId(roleId); + return roleMapper.deleteRoleById(roleId); + } + + /** + * 批量删除角色信息 + * + * @param roleIds 需要删除的角色ID + * @return 结果 + */ + @Override + @Transactional + public int deleteRoleByIds(Long[] roleIds) { + for (Long roleId : roleIds) { + checkRoleAllowed(new SysRole(roleId)); + checkRoleDataScope(roleId); + SysRole role = selectRoleById(roleId); + if (countUserRoleByRoleId(roleId) > 0) { + throw new ServiceException(String.format("%1$s已分配,不能删除", role.getRoleName())); + } + } + // 删除角色与菜单关联 + roleMenuMapper.deleteRoleMenu(roleIds); + // 删除角色与部门关联 + roleDeptMapper.deleteRoleDept(roleIds); + return roleMapper.deleteRoleByIds(roleIds); + } + + /** + * 取消授权用户角色 + * + * @param userRole 用户和角色关联信息 + * @return 结果 + */ + @Override + public int deleteAuthUser(SysUserRole userRole) { + return userRoleMapper.deleteUserRoleInfo(userRole); + } + + /** + * 批量取消授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要取消授权的用户数据ID + * @return 结果 + */ + @Override + public int deleteAuthUsers(Long roleId, Long[] userIds) { + return userRoleMapper.deleteUserRoleInfos(roleId, userIds); + } + + /** + * 批量选择授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要授权的用户数据ID + * @return 结果 + */ + @Override + public int insertAuthUsers(Long roleId, Long[] userIds) { + // 新增用户与角色管理 + List list = new ArrayList(); + for (Long userId : userIds) { + SysUserRole ur = new SysUserRole(); + ur.setUserId(userId); + ur.setRoleId(roleId); + list.add(ur); + } + return userRoleMapper.batchUserRole(list); + } + + @Override + public SysRole selectByRoleKey(String roleKey) { + return roleMapper.selectOne(SysRole::getRoleKey, roleKey); + } + + @Override + public Set getRoleMenuIds(Long roleId) { + // 如果是管理员的情况下,获取全部菜单编号 + SysRole role = selectRoleById(roleId); + if (hasAnySuperAdmin(Collections.singleton(role))) { + return convertSet(menuMapper.selectList(), SysMenu::getMenuId); + } + // 如果是非管理员的情况下,获得拥有的菜单编号 + return convertSet(roleMenuMapper.selectList(new LambdaQueryWrapperX().eq(SysRoleMenu::getRoleId, roleId)), SysRoleMenu::getMenuId); + } + + public boolean hasAnySuperAdmin(Collection roleList) { + if (CollectionUtil.isEmpty(roleList)) { + return false; + } + return roleList.stream().anyMatch(role -> RoleCodeEnum.isSuperAdmin(role.getRoleKey())); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.java new file mode 100644 index 00000000..f80a8771 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.java @@ -0,0 +1,96 @@ +package com.ruoyi.system.service.impl; + +import org.springframework.stereotype.Service; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.domain.SysUserOnline; +import com.ruoyi.system.service.ISysUserOnlineService; + +/** + * 在线用户 服务层处理 + * + * @author ruoyi + */ +@Service +public class SysUserOnlineServiceImpl implements ISysUserOnlineService +{ + /** + * 通过登录地址查询信息 + * + * @param ipaddr 登录地址 + * @param user 用户信息 + * @return 在线用户信息 + */ + @Override + public SysUserOnline selectOnlineByIpaddr(String ipaddr, LoginUser user) + { + if (StringUtils.equals(ipaddr, user.getIpaddr())) + { + return loginUserToUserOnline(user); + } + return null; + } + + /** + * 通过用户名称查询信息 + * + * @param userName 用户名称 + * @param user 用户信息 + * @return 在线用户信息 + */ + @Override + public SysUserOnline selectOnlineByUserName(String userName, LoginUser user) + { + if (StringUtils.equals(userName, user.getUsername())) + { + return loginUserToUserOnline(user); + } + return null; + } + + /** + * 通过登录地址/用户名称查询信息 + * + * @param ipaddr 登录地址 + * @param userName 用户名称 + * @param user 用户信息 + * @return 在线用户信息 + */ + @Override + public SysUserOnline selectOnlineByInfo(String ipaddr, String userName, LoginUser user) + { + if (StringUtils.equals(ipaddr, user.getIpaddr()) && StringUtils.equals(userName, user.getUsername())) + { + return loginUserToUserOnline(user); + } + return null; + } + + /** + * 设置在线用户信息 + * + * @param user 用户信息 + * @return 在线用户 + */ + @Override + public SysUserOnline loginUserToUserOnline(LoginUser user) + { + if (StringUtils.isNull(user) || StringUtils.isNull(user.getUser())) + { + return null; + } + SysUserOnline sysUserOnline = new SysUserOnline(); + sysUserOnline.setTokenId(user.getToken()); + sysUserOnline.setUserName(user.getUsername()); + sysUserOnline.setIpaddr(user.getIpaddr()); + sysUserOnline.setLoginLocation(user.getLoginLocation()); + sysUserOnline.setBrowser(user.getBrowser()); + sysUserOnline.setOs(user.getOs()); + sysUserOnline.setLoginTime(user.getLoginTime()); + if (StringUtils.isNotNull(user.getUser().getDept())) + { + sysUserOnline.setDeptName(user.getUser().getDept().getDeptName()); + } + return sysUserOnline; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java new file mode 100644 index 00000000..65d467ce --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java @@ -0,0 +1,518 @@ +package com.ruoyi.system.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ArrayUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.common.annotation.DataScope; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.bean.BeanValidators; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.system.domain.SysPost; +import com.ruoyi.system.domain.SysUserPost; +import com.ruoyi.system.domain.SysUserRole; +import com.ruoyi.system.mapper.*; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.system.service.ISysUserService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; + +import javax.validation.Validator; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 用户 业务层处理 + * + * @author ruoyi + */ +@Service +public class SysUserServiceImpl extends ServiceImpl implements ISysUserService { + private static final Logger log = LoggerFactory.getLogger(SysUserServiceImpl.class); + + @Autowired + private SysUserMapper userMapper; + + @Autowired + private SysRoleMapper roleMapper; + + @Autowired + private SysPostMapper postMapper; + + @Autowired + private SysUserRoleMapper userRoleMapper; + + @Autowired + private SysUserPostMapper userPostMapper; + + @Autowired + private ISysConfigService configService; + + @Autowired + protected Validator validator; + + /** + * 根据条件分页查询用户列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + @Override + @DataScope(deptAlias = "d", userAlias = "u") + public List selectUserList(SysUser user) { + return userMapper.selectUserList(user); + } + + /** + * 根据条件分页查询已分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + @Override + @DataScope(deptAlias = "d", userAlias = "u") + public List selectAllocatedList(SysUser user) { + return userMapper.selectAllocatedList(user); + } + + /** + * 根据条件分页查询未分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + @Override + @DataScope(deptAlias = "d", userAlias = "u") + public List selectUnallocatedList(SysUser user) { + return userMapper.selectUnallocatedList(user); + } + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + @Override + public SysUser selectUserByUserName(String userName) { + return userMapper.selectUserByUserName(userName); + } + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + @Override + public SysUser selectUserById(Long userId) { + return userMapper.selectUserById(userId); + } + + /** + * 查询用户所属角色组 + * + * @param userName 用户名 + * @return 结果 + */ + @Override + public String selectUserRoleGroup(String userName) { + List list = roleMapper.selectRolesByUserName(userName); + if (CollectionUtils.isEmpty(list)) { + return StringUtils.EMPTY; + } + return list.stream().map(SysRole::getRoleName).collect(Collectors.joining(",")); + } + + /** + * 查询用户所属岗位组 + * + * @param userName 用户名 + * @return 结果 + */ + @Override + public String selectUserPostGroup(String userName) { + List list = postMapper.selectPostsByUserName(userName); + if (CollectionUtils.isEmpty(list)) { + return StringUtils.EMPTY; + } + return list.stream().map(SysPost::getPostName).collect(Collectors.joining(",")); + } + + /** + * 校验用户名称是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public String checkUserNameUnique(SysUser user) { + Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId(); + SysUser info = userMapper.checkUserNameUnique(user.getUserName()); + if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验手机号码是否唯一 + * + * @param user 用户信息 + * @return + */ + @Override + public String checkPhoneUnique(SysUser user) { + Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId(); + SysUser info = userMapper.checkPhoneUnique(user.getPhonenumber()); + if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验email是否唯一 + * + * @param user 用户信息 + * @return + */ + @Override + public String checkEmailUnique(SysUser user) { + Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId(); + SysUser info = userMapper.checkEmailUnique(user.getEmail()); + if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验用户是否允许操作 + * + * @param user 用户信息 + */ + @Override + public void checkUserAllowed(SysUser user) { + if (StringUtils.isNotNull(user.getUserId()) && user.isAdmin()) { + throw new ServiceException("不允许操作超级管理员用户"); + } + } + + /** + * 校验用户是否有数据权限 + * + * @param userId 用户id + */ + @Override + public void checkUserDataScope(Long userId) { + if (!SysUser.isAdmin(SecurityUtils.getUserId())) { + SysUser user = new SysUser(); + user.setUserId(userId); + List users = SpringUtils.getAopProxy(this).selectUserList(user); + if (StringUtils.isEmpty(users)) { + throw new ServiceException("没有权限访问用户数据!"); + } + } + } + + /** + * 新增保存用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + @Transactional + public int insertUser(SysUser user) { + // 新增用户信息 + int rows = userMapper.insertUser(user); + // 新增用户岗位关联 + insertUserPost(user); + // 新增用户与角色管理 + insertUserRole(user); + return rows; + } + + /** + * 注册用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public boolean registerUser(SysUser user) { + return userMapper.insertUser(user) > 0; + } + + /** + * 修改保存用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + @Transactional + public int updateUser(SysUser user) { + Long userId = user.getUserId(); + // 删除用户与角色关联 + userRoleMapper.deleteUserRoleByUserId(userId); + // 新增用户与角色管理 + insertUserRole(user); + // 删除用户与岗位关联 + userPostMapper.deleteUserPostByUserId(userId); + // 新增用户与岗位管理 + insertUserPost(user); + return userMapper.updateUser(user); + } + + /** + * 用户授权角色 + * + * @param userId 用户ID + * @param roleIds 角色组 + */ + @Override + @Transactional + public void insertUserAuth(Long userId, Long[] roleIds) { + userRoleMapper.deleteUserRoleByUserId(userId); + insertUserRole(userId, roleIds); + } + + /** + * 修改用户状态 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public int updateUserStatus(SysUser user) { + return userMapper.updateUser(user); + } + + /** + * 修改用户基本信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public int updateUserProfile(SysUser user) { + return userMapper.updateUser(user); + } + + /** + * 修改用户头像 + * + * @param userName 用户名 + * @param avatar 头像地址 + * @return 结果 + */ + @Override + public boolean updateUserAvatar(String userName, String avatar) { + return userMapper.updateUserAvatar(userName, avatar) > 0; + } + + /** + * 重置用户密码 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public int resetPwd(SysUser user) { + return userMapper.updateUser(user); + } + + /** + * 重置用户密码 + * + * @param userName 用户名 + * @param password 密码 + * @return 结果 + */ + @Override + public int resetUserPwd(String userName, String password) { + return userMapper.resetUserPwd(userName, password); + } + + /** + * 新增用户角色信息 + * + * @param user 用户对象 + */ + public void insertUserRole(SysUser user) { + this.insertUserRole(user.getUserId(), user.getRoleIds()); + } + + /** + * 新增用户岗位信息 + * + * @param user 用户对象 + */ + public void insertUserPost(SysUser user) { + Long[] posts = user.getPostIds(); + if (StringUtils.isNotEmpty(posts)) { + // 新增用户与岗位管理 + List list = new ArrayList(posts.length); + for (Long postId : posts) { + SysUserPost up = new SysUserPost(); + up.setUserId(user.getUserId()); + up.setPostId(postId); + list.add(up); + } + userPostMapper.batchUserPost(list); + } + } + + /** + * 新增用户角色信息 + * + * @param userId 用户ID + * @param roleIds 角色组 + */ + public void insertUserRole(Long userId, Long[] temps) { + Long[] roleIds = ArrayUtil.removeNull(temps); + if (StringUtils.isNotEmpty(roleIds)) { + // 新增用户与角色管理 + List list = new ArrayList(roleIds.length); + for (Long roleId : roleIds) { + SysUserRole ur = new SysUserRole(); + ur.setUserId(userId); + ur.setRoleId(roleId); + list.add(ur); + } + userRoleMapper.batchUserRole(list); + } + } + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * @return 结果 + */ + @Override + @Transactional + public int deleteUserById(Long userId) { + // 删除用户与角色关联 + userRoleMapper.deleteUserRoleByUserId(userId); + // 删除用户与岗位表 + userPostMapper.deleteUserPostByUserId(userId); + return userMapper.deleteUserById(userId); + } + + /** + * 批量删除用户信息 + * + * @param userIds 需要删除的用户ID + * @return 结果 + */ + @Override + @Transactional + public int deleteUserByIds(Long[] userIds) { + for (Long userId : userIds) { + checkUserAllowed(new SysUser(userId)); + checkUserDataScope(userId); + } + // 删除用户与角色关联 + userRoleMapper.deleteUserRole(userIds); + // 删除用户与岗位关联 + userPostMapper.deleteUserPost(userIds); + return userMapper.deleteUserByIds(userIds); + } + + /** + * 导入用户数据 + * + * @param userList 用户数据列表 + * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据 + * @param operName 操作用户 + * @return 结果 + */ + @Override + public String importUser(List userList, Boolean isUpdateSupport, String operName) { + if (StringUtils.isNull(userList) || userList.size() == 0) { + throw new ServiceException("导入用户数据不能为空!"); + } + int successNum = 0; + int failureNum = 0; + StringBuilder successMsg = new StringBuilder(); + StringBuilder failureMsg = new StringBuilder(); + String password = configService.selectConfigByKey("sys.user.initPassword"); + for (SysUser user : userList) { + try { + // 验证是否存在这个用户 + SysUser u = userMapper.selectUserByUserName(user.getUserName()); + if (StringUtils.isNull(u)) { + BeanValidators.validateWithException(validator, user); + user.setPassword(SecurityUtils.encryptPassword(password)); + user.setCreateBy(operName); + this.insertUser(user); + successNum++; + successMsg.append("
" + successNum + "、账号 " + user.getUserName() + " 导入成功"); + } else if (isUpdateSupport) { + BeanValidators.validateWithException(validator, user); + checkUserAllowed(user); + checkUserDataScope(user.getUserId()); + user.setUpdateBy(operName); + this.updateUser(user); + successNum++; + successMsg.append("
" + successNum + "、账号 " + user.getUserName() + " 更新成功"); + } else { + failureNum++; + failureMsg.append("
" + failureNum + "、账号 " + user.getUserName() + " 已存在"); + } + } catch (Exception e) { + failureNum++; + String msg = "
" + failureNum + "、账号 " + user.getUserName() + " 导入失败:"; + failureMsg.append(msg + e.getMessage()); + log.error(msg, e); + } + } + if (failureNum > 0) { + failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:"); + throw new ServiceException(failureMsg.toString()); + } else { + successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:"); + } + return successMsg.toString(); + } + + @Override + public List getUsersByPostIds(Collection postIds) { + if (CollUtil.isEmpty(postIds)) { + return Collections.emptyList(); + } + List sysUserPosts = userPostMapper.selectList(Wrappers.query().lambda().in(SysUserPost::getPostId, postIds)); + Set userIds = sysUserPosts.stream().map(sysUserPost -> sysUserPost.getUserId()).collect(Collectors.toSet()); + if (CollUtil.isEmpty(userIds)) { + return Collections.emptyList(); + } + return userMapper.selectBatchIds(userIds); + } + + @Override + public Set getUserRoleIdListByRoleIds(Collection roleIds) { + List sysUserRoles = userRoleMapper.selectList(Wrappers.query().lambda().in(SysUserRole::getRoleId, roleIds)); + Set userIds = sysUserRoles.stream().map(sysUserRole -> sysUserRole.getUserId()).collect(Collectors.toSet()); + if (CollUtil.isEmpty(userIds)) { + return Collections.emptySet(); + } else { + return userIds; + } + } + @Override + public List getUserListByStatus(Integer status) { + return userMapper.selectListByStatus(status); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TenantPackageServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TenantPackageServiceImpl.java new file mode 100644 index 00000000..e03e9370 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TenantPackageServiceImpl.java @@ -0,0 +1,115 @@ +package com.ruoyi.system.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.ruoyi.common.enums.CommonStatusEnum; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.system.convert.tenant.TenantPackageConvert; +import com.ruoyi.system.domain.TenantDO; +import com.ruoyi.system.domain.TenantPackageDO; +import com.ruoyi.system.domain.vo.packages.TenantPackageCreateReqVO; +import com.ruoyi.system.domain.vo.packages.TenantPackagePageReqVO; +import com.ruoyi.system.domain.vo.packages.TenantPackageUpdateReqVO; +import com.ruoyi.system.mapper.TenantPackageMapper; +import com.ruoyi.system.service.TenantPackageService; +import com.ruoyi.system.service.TenantService; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.util.List; +import static com.ruoyi.common.constant.ErrorCodeConstants.*; +/** + * 租户套餐 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class TenantPackageServiceImpl implements TenantPackageService { + + @Resource + private TenantPackageMapper tenantPackageMapper; + + @Resource + @Lazy // 避免循环依赖的报错 + private TenantService tenantService; + + @Override + public Long createTenantPackage(TenantPackageCreateReqVO createReqVO) { + // 插入 + TenantPackageDO tenantPackage = TenantPackageConvert.INSTANCE.convert(createReqVO); + tenantPackageMapper.insert(tenantPackage); + // 返回 + return tenantPackage.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateTenantPackage(TenantPackageUpdateReqVO updateReqVO) { + // 校验存在 + TenantPackageDO tenantPackage = validateTenantPackageExists(updateReqVO.getId()); + // 更新 + TenantPackageDO updateObj = TenantPackageConvert.INSTANCE.convert(updateReqVO); + tenantPackageMapper.updateById(updateObj); + // 如果菜单发生变化,则修改每个租户的菜单 + if (!CollUtil.isEqualList(tenantPackage.getMenuIds(), updateReqVO.getMenuIds())) { + List tenants = tenantService.getTenantListByPackageId(tenantPackage.getId()); + tenants.forEach(tenant -> tenantService.updateTenantRoleMenu(tenant.getId(), updateReqVO.getMenuIds())); + } + } + + @Override + public void deleteTenantPackage(Long id) { + // 校验存在 + this.validateTenantPackageExists(id); + // 校验正在使用 + this.validateTenantUsed(id); + // 删除 + tenantPackageMapper.deleteById(id); + } + + private TenantPackageDO validateTenantPackageExists(Long id) { + TenantPackageDO tenantPackage = tenantPackageMapper.selectById(id); + if (tenantPackage == null) { + throw new ServiceException(TENANT_PACKAGE_NOT_EXISTS.getMsg()); + } + return tenantPackage; + } + + private void validateTenantUsed(Long id) { + if (tenantService.getTenantCountByPackageId(id) > 0) { + throw new ServiceException(TENANT_PACKAGE_USED.getMsg()); + } + } + + @Override + public TenantPackageDO getTenantPackage(Long id) { + return tenantPackageMapper.selectById(id); + } + + @Override + public PageResult getTenantPackagePage(TenantPackagePageReqVO pageReqVO) { + return tenantPackageMapper.selectPage(pageReqVO); + } + + @Override + public TenantPackageDO validTenantPackage(Long id) { + TenantPackageDO tenantPackage = tenantPackageMapper.selectById(id); + if (tenantPackage == null) { + throw new ServiceException(TENANT_PACKAGE_NOT_EXISTS.getMsg()); + } + if (tenantPackage.getStatus().equals(CommonStatusEnum.DISABLE.getStatus())) { + throw new ServiceException("名字为【"+tenantPackage.getName()+"】的租户套餐已被禁用"); + } + return tenantPackage; + } + + @Override + public List getTenantPackageListByStatus(Integer status) { + return tenantPackageMapper.selectListByStatus(status); + } + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TenantServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TenantServiceImpl.java new file mode 100644 index 00000000..819cc10a --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TenantServiceImpl.java @@ -0,0 +1,317 @@ +package com.ruoyi.system.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.core.domain.entity.SysDictData; +import com.ruoyi.common.core.domain.entity.SysMenu; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.enums.CommonStatusEnum; +import com.ruoyi.common.enums.RoleCodeEnum; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.DictUtils; +import com.ruoyi.common.utils.EhcacheUtil; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.collection.CollectionUtils; +import com.ruoyi.plugin.tenant.config.TenantProperties; +import com.ruoyi.plugin.tenant.core.context.TenantContextHolder; +import com.ruoyi.plugin.tenant.core.util.TenantUtils; +import com.ruoyi.system.convert.tenant.TenantConvert; +import com.ruoyi.system.domain.TenantDO; +import com.ruoyi.system.domain.TenantPackageDO; +import com.ruoyi.system.domain.vo.tenant.TenantCreateReqVO; +import com.ruoyi.system.domain.vo.tenant.TenantExportReqVO; +import com.ruoyi.system.domain.vo.tenant.TenantPageReqVO; +import com.ruoyi.system.domain.vo.tenant.TenantUpdateReqVO; +import com.ruoyi.system.mapper.TenantMapper; +import com.ruoyi.system.service.*; +import com.ruoyi.system.service.handler.TenantInfoHandler; +import com.ruoyi.system.service.handler.TenantMenuHandler; +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.ss.formula.functions.T; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.util.*; +import java.util.stream.Collectors; + +import static com.ruoyi.common.constant.ErrorCodeConstants.*; + +/** + * 租户 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +@Slf4j +public class TenantServiceImpl implements TenantService { + + @SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection") + @Autowired(required = false) // 由于 yudao.tenant.enable 配置项,可以关闭多租户的功能,所以这里只能不强制注入 + private TenantProperties tenantProperties; + + @Resource + private TenantMapper tenantMapper; + + @Resource + private TenantPackageService tenantPackageService; + @Resource + @Lazy // 延迟,避免循环依赖报错 + private ISysUserService userService; + @Resource + private ISysRoleService roleService; + @Resource + private ISysMenuService menuService; +// @Resource +// private PermissionService permissionService; + + /** + * 项目启动时,初始化租户到缓存 + */ + @PostConstruct + public void init() + { + loadingTenantCache(); + } + + private void loadingTenantCache() { + List tenants = tenantMapper.selectList(new LambdaQueryWrapper().eq(TenantDO::getStatus, 0).eq(TenantDO::getDeleted, 0)); + for (TenantDO tenant : tenants) { + EhcacheUtil.put("tenant", getCacheKey(tenant.getId().toString()), tenant); + } + } + /** + * 设置cache key + * + * @param tenantId 参数键 + * @return 缓存键key + */ + public static String getCacheKey(String tenantId) + { + return CacheConstants.SYS_TENANT_KEY + tenantId; + } + @Override + public List getTenantIds() { + List tenants = tenantMapper.selectList(); + return CollectionUtils.convertList(tenants, TenantDO::getId); + } + + @Override + public void validTenant(Long id) { + TenantDO tenant = getTenant(id); + if (tenant == null) { + throw new ServiceException(TENANT_NOT_EXISTS.getMsg()); + } + if (tenant.getStatus().equals(CommonStatusEnum.DISABLE.getStatus())) { + throw new ServiceException(String.format(TENANT_DISABLE.getMsg(), tenant.getName())); + } + + if (DateUtils.isExpired(tenant.getExpireTime())) { + throw new ServiceException(String.format(TENANT_EXPIRE.getMsg(), tenant.getName())); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createTenant(TenantCreateReqVO createReqVO) { + // 校验套餐被禁用 + TenantPackageDO tenantPackage = tenantPackageService.validTenantPackage(createReqVO.getPackageId()); + + // 创建租户 + TenantDO tenant = TenantConvert.INSTANCE.convert(createReqVO); + tenantMapper.insert(tenant); + EhcacheUtil.put("tenant", getCacheKey(tenant.getId().toString()), tenant); + TenantUtils.execute(tenant.getId(), () -> { + // 创建角色 + Long roleId = createRole(tenantPackage); + // 创建用户,并分配角色 + Long userId = createUser(roleId, createReqVO); + // 修改租户的管理员 + TenantDO tenantDO = new TenantDO(); + tenantDO.setId(tenant.getId()); + tenantDO.setContactUserId(userId); + tenantMapper.updateById(tenantDO); + }); + return tenant.getId(); + } + + private Long createUser(Long roleId, TenantCreateReqVO createReqVO) { + SysUser user = userService.selectUserByUserName(createReqVO.getUsername()); + if (user != null) { + throw new ServiceException("租户已存在", HttpStatus.ERROR); + } + user = new SysUser(); + user.setUserName(createReqVO.getUsername()); + user.setStatus("0"); + user.setPassword(SecurityUtils.encryptPassword(createReqVO.getPassword())); + user.setNickName(createReqVO.getContactName()); + user.setPhonenumber(createReqVO.getContactMobile()); + user.setRoleIds(new Long[]{roleId}); + userService.insertUser(user); + return user.getUserId(); + } + + private Long createRole(TenantPackageDO tenantPackage) { + // 创建角色 + SysRole role = roleService.selectByRoleKey("tenant_admin"); + if (role == null) { + role = new SysRole(); + role.setRoleName("租户管理员"); + role.setRoleKey("tenant_admin"); + role.setRoleSort("0"); + role.setDataScope("1"); + role.setStatus("0"); + role.setMenuIds(tenantPackage.getMenuIds().toArray(new Long[tenantPackage.getMenuIds().size()])); + roleService.insertRole(role); + }else{ + role.setMenuIds(tenantPackage.getMenuIds().toArray(new Long[tenantPackage.getMenuIds().size()])); + roleService.updateRole(role); + } + return role.getRoleId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateTenant(TenantUpdateReqVO updateReqVO) { + // 校验存在 + TenantDO tenant = checkUpdateTenant(updateReqVO.getId()); + // 校验套餐被禁用 + TenantPackageDO tenantPackage = tenantPackageService.validTenantPackage(updateReqVO.getPackageId()); + EhcacheUtil.put("tenant", getCacheKey(tenant.getId().toString()), tenant); + // 更新租户 + TenantDO updateObj = TenantConvert.INSTANCE.convert(updateReqVO); + tenantMapper.updateById(updateObj); + // 如果套餐发生变化,则修改其角色的权限 + if (ObjectUtil.notEqual(tenant.getPackageId(), updateReqVO.getPackageId())) { + updateTenantRoleMenu(tenant.getId(), tenantPackage.getMenuIds()); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateTenantRoleMenu(Long tenantId, Set menuIds) { + TenantUtils.execute(tenantId, () -> { + // 获得所有角色 + List roles = roleService.list(null); + roles.forEach(role -> Assert.isTrue(tenantId.equals(role.getTenantId()), "角色({}/{}) 租户不匹配", role.getRoleId(), role.getTenantId(), tenantId)); // 兜底校验 + // 重新分配每个角色的权限 + roles.forEach(role -> { + // 如果是租户管理员,重新分配其权限为租户套餐的权限 + if (Objects.equals(role.getRoleKey(), RoleCodeEnum.TENANT_ADMIN.getCode())) { + role.setMenuIds(menuIds.toArray(new Long[menuIds.size()])); + roleService.updateRole(role); + log.info("[updateTenantRoleMenu][租户管理员({}/{}) 的权限修改为({})]", role.getRoleId(), role.getTenantId(), menuIds); + return; + } + // 如果是其他角色,则去掉超过套餐的权限 + Set roleMenuIds = roleService.getRoleMenuIds(role.getRoleId()); + roleMenuIds = CollUtil.intersectionDistinct(roleMenuIds, menuIds); + role.setMenuIds(roleMenuIds.toArray(new Long[roleMenuIds.size()])); + roleService.updateRole(role); + log.info("[updateTenantRoleMenu][角色({}/{}) 的权限修改为({})]", role.getRoleId(), role.getTenantId(), roleMenuIds); + }); + }); + } + + @Override + public void deleteTenant(Long id) { + // 校验存在 + checkUpdateTenant(id); + // 删除 + tenantMapper.deleteById(id); + EhcacheUtil.remove("tenant", getCacheKey(id.toString())); + } + + private TenantDO checkUpdateTenant(Long id) { + TenantDO tenant = tenantMapper.selectById(id); + if (tenant == null) { + throw new ServiceException(TENANT_NOT_EXISTS.getMsg()); + } + // 内置租户,不允许删除 + if (isSystemTenant(tenant)) { + throw new ServiceException(TENANT_CAN_NOT_UPDATE_SYSTEM.getMsg()); + } + return tenant; + } + + @Override + public TenantDO getTenant(Long id) { + return tenantMapper.selectById(id); + } + + @Override + public PageResult getTenantPage(TenantPageReqVO pageReqVO) { + return tenantMapper.selectPage(pageReqVO); + } + + @Override + public List getTenantList(TenantExportReqVO exportReqVO) { + return tenantMapper.selectList(exportReqVO); + } + + @Override + public TenantDO getTenantByName(String name) { + return tenantMapper.selectByName(name); + } + + @Override + public Long getTenantCountByPackageId(Long packageId) { + return tenantMapper.selectCountByPackageId(packageId); + } + + @Override + public List getTenantListByPackageId(Long packageId) { + return tenantMapper.selectListByPackageId(packageId); + } + + @Override + public void handleTenantInfo(TenantInfoHandler handler) { + // 如果禁用,则不执行逻辑 + if (isTenantDisable()) { + return; + } + // 获得租户 + TenantDO tenant = getTenant(TenantContextHolder.getRequiredTenantId()); + // 执行处理器 + handler.handle(tenant); + } + + @Override + public void handleTenantMenu(TenantMenuHandler handler) { + // 如果禁用,则不执行逻辑 + if (isTenantDisable()) { + return; + } + // 获得租户,然后获得菜单 + TenantDO tenant = getTenant(TenantContextHolder.getRequiredTenantId()); + Set menuIds; + if (isSystemTenant(tenant)) { // 系统租户,菜单是全量的 + menuIds = CollectionUtils.convertSet(menuService.list(), SysMenu::getMenuId); + } else { + menuIds = tenantPackageService.getTenantPackage(tenant.getPackageId()).getMenuIds(); + } + // 执行处理器 + handler.handle(menuIds); + } + + private static boolean isSystemTenant(TenantDO tenant) { + return Objects.equals(tenant.getPackageId(), TenantDO.PACKAGE_ID_SYSTEM); + } + + private boolean isTenantDisable() { + return tenantProperties == null || Boolean.FALSE.equals(tenantProperties.getEnable()); + } + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TreeDictDataServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TreeDictDataServiceImpl.java new file mode 100644 index 00000000..d06fb4f6 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TreeDictDataServiceImpl.java @@ -0,0 +1,231 @@ +package com.ruoyi.system.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.TreeConstants; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.TreeUtils; +import com.ruoyi.system.domain.SysTreeDict; +import com.ruoyi.system.domain.SysTreeDictData; +import com.ruoyi.system.mapper.SysTreeDictDataMapper; +import com.ruoyi.system.service.ISysTreeDictDataService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Arrays; +import java.util.List; + +/** + * 树形字典数据Service业务层处理 + * + * @author wangzongrun + * @date 2021-05-31 + */ +@Service +public class TreeDictDataServiceImpl implements ISysTreeDictDataService { + + @Autowired + private SysTreeDictDataMapper sysTreeDictDataMapper; + + /** + * 查询树形字典数据 + * + * @param id 树形字典数据ID + * @return 树形字典数据 + */ + @Override + public SysTreeDictData selectById(String id) { + return sysTreeDictDataMapper.selectById(id); + } + + /** + * 分页查询树形字典数据列表 + * + * @param sysTreeDictData 树形字典数据 + * @return 树形字典数据 + */ + @Override + public IPage selectList(IPage page, SysTreeDictData sysTreeDictData) { + return sysTreeDictDataMapper.selectTreeDictDataPage(page, sysTreeDictData); + } + + /** + * 查询所有树形字典数据列表 + * + * @param sysTreeDictData 树形字典数据 + * @return 树形字典数据 + */ + @Override + public List selectListAll(SysTreeDictData sysTreeDictData) { + return sysTreeDictDataMapper.selectTreeDictDataList(sysTreeDictData); + } + + /** + * 查询所有树形字典数据树结构 + * fsd + * + * @param sysTreeDictData 树形字典数据 + * @return 树结构 + */ + @Override + public List buildTree(SysTreeDictData sysTreeDictData) { + List list = sysTreeDictDataMapper.selectTreeDictDataList(sysTreeDictData); + TreeUtils treeUtils = new TreeUtils(); + List result = treeUtils.buildTree(list); + return result; + } + + /** + * 新增树形字典数据 + * + * @param sysTreeDictData 树形字典数据 + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int insert(SysTreeDictData sysTreeDictData) { + // 更新父类节点 + SysTreeDictData parent = new SysTreeDictData(); + parent.setId(sysTreeDictData.getPid()); + parent.setIsLeaf(Constants.FALSE); + sysTreeDictDataMapper.updateById(parent); + + // 插入实体 + return sysTreeDictDataMapper.insert(sysTreeDictData); + } + + /** + * 修改树形字典数据 + * + * @param sysTreeDictData 树形字典数据 + * @return 结果 + */ + @Override + public int update(SysTreeDictData sysTreeDictData) { + return sysTreeDictDataMapper.updateById(sysTreeDictData); + } + + /** + * 批量删除树形字典数据 + * + * @param sids 需要删除的树形字典数据ID + * @return 结果 + */ + @Override + public int deleteByIds(String[] sids) { + return sysTreeDictDataMapper.deleteBatchIds(Arrays.asList(sids)); + } + + /** + * 删除树形字典数据信息 + * + * @param id 树形字典数据ID + * @return 结果 + */ + @Override + public int deleteById(String id) { + return sysTreeDictDataMapper.deleteById(id); + } + + /** + * 检查名称唯一性 + * + * @param entity 查询条件 + * @return 结果 + */ + @Override + public AjaxResult checkUniqueByLabel(SysTreeDictData entity) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper(); + String parentId = TreeConstants.PARENT_ROOT_VALUE; + // 如果传入数据名称为空的情况下,返回null + if (StringUtils.isEmpty(entity.getLabel())) { + return null; + } + + // 增加查询条件:名称, 分组KEY + queryWrapper.eq(SysTreeDictData::getLabel, entity.getLabel()); + queryWrapper.eq(SysTreeDictData::getTreeDict, entity.getTreeDict()); + + // 同级层次下不能重名 + if (StringUtils.isNotNull(entity.getPid())) { + parentId = entity.getPid(); + } + queryWrapper.eq(SysTreeDictData::getPid, parentId); + + + // 如果存在ID,排除本身ID的实体存在 + if (StringUtils.isNotNull(entity.getId())) { + queryWrapper.ne(SysTreeDictData::getId, entity.getId()); + } + + SysTreeDictData result = sysTreeDictDataMapper.selectOne(queryWrapper); + + if (StringUtils.isNotNull(result)) { + return AjaxResult.error(501, "名称:" + entity.getLabel() + " 重复"); + } else { + return AjaxResult.success(); + } + } + + @Override + public List selectTreeDictByType(String treeDict) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(SysTreeDictData::getTreeDict, treeDict); + sysTreeDictDataMapper.selectList(wrapper); + return null; + } + + @Override + public AjaxResult checkCode(SysTreeDictData sysTreeDictData) { + String code = sysTreeDictData.getCode(); + if (code.length() <= 0) { + return AjaxResult.error(501, "不能为空"); + } + if (checkUniqueCode(sysTreeDictData)) { + return AjaxResult.error(502, "编码重复!" ); + } + return AjaxResult.success(sysTreeDictData); + } + + private String getPLevelCode(String pid) { + String pLevelCode = ""; + if (!TreeConstants.PARENT_ROOT_VALUE.equals(pid)) { + SysTreeDictData parent = sysTreeDictDataMapper.selectById(pid); + pLevelCode = parent.getLevelCode(); + } + return pLevelCode; + } + + private String getLevelCode(SysTreeDictData sysTreeDictData, String pid) { + String levelCode = ""; + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper(); + queryWrapper.eq(SysTreeDictData::getTreeDict, sysTreeDictData.getTreeDict()); + queryWrapper.eq(SysTreeDictData::getPid, pid); + queryWrapper.orderByDesc(SysTreeDictData::getLevelCode); + List sibling = sysTreeDictDataMapper.selectList(queryWrapper); + if (sibling.size() > 0) { + levelCode = sibling.get(0).getLevelCode(); + } + return levelCode; + } + + private boolean checkUniqueCode(SysTreeDictData entity) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper(); + + // 增加查询条件:编码, 分组KEY + queryWrapper.eq(SysTreeDictData::getCode, entity.getCode()); + queryWrapper.eq(SysTreeDictData::getTreeDict, entity.getTreeDict()); + + // 如果存在ID,排除本身ID的实体存在 + if (StringUtils.isNotNull(entity.getId())) { + queryWrapper.ne(SysTreeDictData::getId, entity.getId()); + } + + SysTreeDictData result = sysTreeDictDataMapper.selectOne(queryWrapper); + + return StringUtils.isNotNull(result); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TreeDictServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TreeDictServiceImpl.java new file mode 100644 index 00000000..659575f8 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TreeDictServiceImpl.java @@ -0,0 +1,124 @@ +package com.ruoyi.system.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.utils.TreeUtils; +import com.ruoyi.system.domain.SysTreeDict; +import com.ruoyi.system.domain.SysTreeDictData; +import com.ruoyi.system.mapper.SysTreeDictDataMapper; +import com.ruoyi.system.mapper.SysTreeDictMapper; +import com.ruoyi.system.service.ISysTreeDictService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import java.util.Arrays; +import java.util.List; + +/** + * 系统树形Service业务层处理 + * + * @author wangzongrun + * @date 2021-06-04 + */ +@Service +public class TreeDictServiceImpl implements ISysTreeDictService { + + @Autowired + private SysTreeDictMapper sysTreeDictMapper; + + @Autowired + private SysTreeDictDataMapper sysTreeDictDataMapper; + + /** + * 项目启动时,初始化分类树到缓存 + */ + @PostConstruct + public void init() { + List list = sysTreeDictMapper.selectList(null); + list.stream().forEach(dictData -> { + List dictDatas =sysTreeDictDataMapper.selectList(new QueryWrapper().lambda().eq(SysTreeDictData::getDelFlag, UserConstants.NORMAL) + .eq(SysTreeDictData::getTreeDict, dictData.getCode()).orderByAsc(SysTreeDictData::getOrderNum).orderByAsc(SysTreeDictData::getLevelCode)); + List buildTree = new TreeUtils().buildTree(dictDatas); + TreeUtils.initCacheTreeData(dictData.getCode(),buildTree); + }); + } + + /** + * 查询系统树形 + * + * @param id 系统树形ID + * @return 系统树形 + */ + @Override + public SysTreeDict selectById(String id) { + return sysTreeDictMapper.selectById(id); + } + + /** + * 分页查询系统树形列表 + * + * @param sysTreeDict 系统树形 + * @return 系统树形 + */ + @Override + public IPage selectList(IPage page, SysTreeDict sysTreeDict) { + return sysTreeDictMapper.selectTreeDictPage(page, sysTreeDict); + } + + /** + * 查询所有系统树形列表 + * + * @param sysTreeDict 系统树形 + * @return 系统树形 + */ + @Override + public List selectListAll(SysTreeDict sysTreeDict) { + return sysTreeDictMapper.selectTreeDictList(sysTreeDict); + } + + /** + * 新增系统树形 + * + * @param sysTreeDict 系统树形 + * @return 结果 + */ + @Override + public int insert(SysTreeDict sysTreeDict) { + return sysTreeDictMapper.insert(sysTreeDict); + } + + /** + * 修改系统树形 + * + * @param sysTreeDict 系统树形 + * @return 结果 + */ + @Override + public int update(SysTreeDict sysTreeDict) { + return sysTreeDictMapper.updateById(sysTreeDict); + } + + /** + * 批量删除系统树形 + * + * @param sids 需要删除的系统树形ID + * @return 结果 + */ + @Override + public int deleteByIds(String[] sids) { + return sysTreeDictMapper.deleteBatchIds(Arrays.asList(sids)); + } + + /** + * 删除系统树形信息 + * + * @param id 系统树形ID + * @return 结果 + */ + @Override + public int deleteById(String id) { + return sysTreeDictMapper.deleteById(id); + } +} diff --git a/ruoyi-system/src/main/resources/mapper/system/SysConfigMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysConfigMapper.xml new file mode 100644 index 00000000..8b979061 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysConfigMapper.xml @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + select config_id, config_name, config_key, config_value, config_type, create_by, create_time, update_by, update_time, remark + from sys_config + + + + + + + and config_id = #{configId} + + + and config_key = #{configKey} + + + + + + + + + + + + insert into sys_config ( + config_name, + config_key, + config_value, + config_type, + create_by, + remark, + create_time + )values( + #{configName}, + #{configKey}, + #{configValue}, + #{configType}, + #{createBy}, + #{remark}, + sysdate() + ) + + + + update sys_config + + config_name = #{configName}, + config_key = #{configKey}, + config_value = #{configValue}, + config_type = #{configType}, + update_by = #{updateBy}, + remark = #{remark}, + update_time = sysdate() + + where config_id = #{configId} + + + + delete from sys_config where config_id = #{configId} + + + + delete from sys_config where config_id in + + #{configId} + + + + \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml new file mode 100644 index 00000000..1786c907 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + select d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.phone, d.email, d.status, d.del_flag, d.create_by, d.create_time + from sys_dept d + + + + + + + + + + + + + + + + + + + + insert into sys_dept( + dept_id, + parent_id, + dept_name, + ancestors, + order_num, + leader, + phone, + email, + status, + create_by, + create_time + )values( + #{deptId}, + #{parentId}, + #{deptName}, + #{ancestors}, + #{orderNum}, + #{leader}, + #{phone}, + #{email}, + #{status}, + #{createBy}, + sysdate() + ) + + + + update sys_dept + + parent_id = #{parentId}, + dept_name = #{deptName}, + ancestors = #{ancestors}, + order_num = #{orderNum}, + leader = #{leader}, + phone = #{phone}, + email = #{email}, + status = #{status}, + update_by = #{updateBy}, + update_time = sysdate() + + where dept_id = #{deptId} + + + + update sys_dept set ancestors = + + when #{item.deptId} then #{item.ancestors} + + where dept_id in + + #{item.deptId} + + + + + update sys_dept set status = '0' where dept_id in + + #{deptId} + + + + + update sys_dept set del_flag = '2' where dept_id = #{deptId} + + + diff --git a/ruoyi-system/src/main/resources/mapper/system/SysDictDataMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysDictDataMapper.xml new file mode 100644 index 00000000..8da9030b --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysDictDataMapper.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + select dict_code, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark + from sys_dict_data + + + + + + + + + + + + + + delete from sys_dict_data where dict_code = #{dictCode} + + + + delete from sys_dict_data where dict_code in + + #{dictCode} + + + + + update sys_dict_data + + dict_sort = #{dictSort}, + dict_label = #{dictLabel}, + dict_value = #{dictValue}, + dict_type = #{dictType}, + css_class = #{cssClass}, + list_class = #{listClass}, + is_default = #{isDefault}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where dict_code = #{dictCode} + + + + update sys_dict_data set dict_type = #{newDictType} where dict_type = #{oldDictType} + + + + insert into sys_dict_data( + dict_sort, + dict_label, + dict_value, + dict_type, + css_class, + list_class, + is_default, + status, + remark, + create_by, + create_time + )values( + #{dictSort}, + #{dictLabel}, + #{dictValue}, + #{dictType}, + #{cssClass}, + #{listClass}, + #{isDefault}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysDictTypeMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysDictTypeMapper.xml new file mode 100644 index 00000000..55b4075f --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysDictTypeMapper.xml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + select dict_id, dict_name, dict_type, status, create_by, create_time, remark + from sys_dict_type + + + + + + + + + + + + + + delete from sys_dict_type where dict_id = #{dictId} + + + + delete from sys_dict_type where dict_id in + + #{dictId} + + + + + update sys_dict_type + + dict_name = #{dictName}, + dict_type = #{dictType}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where dict_id = #{dictId} + + + + insert into sys_dict_type( + dict_name, + dict_type, + status, + remark, + create_by, + create_time + )values( + #{dictName}, + #{dictType}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysLogininforMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysLogininforMapper.xml new file mode 100644 index 00000000..b8178fa3 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysLogininforMapper.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + insert into sys_logininfor (user_name, status, ipaddr, login_location, browser, os, msg, login_time) + values (#{userName}, #{status}, #{ipaddr}, #{loginLocation}, #{browser}, #{os}, #{msg}, sysdate()) + + + + + + delete from sys_logininfor where info_id in + + #{infoId} + + + + + truncate table sys_logininfor + + + \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml new file mode 100644 index 00000000..eb995204 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + select menu_id, menu_name, parent_id, order_num, path, component, `query`, is_frame, is_cache, menu_type, visible, status, ifnull(perms,'') as perms, icon, create_time + from sys_menu + + + + + + + + + + + + + + + + + + + + + + + + + + update sys_menu + + menu_name = #{menuName}, + parent_id = #{parentId}, + order_num = #{orderNum}, + path = #{path}, + component = #{component}, + `query` = #{query}, + is_frame = #{isFrame}, + is_cache = #{isCache}, + menu_type = #{menuType}, + visible = #{visible}, + status = #{status}, + perms = #{perms}, + icon = #{icon}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where menu_id = #{menuId} + + + + insert into sys_menu( + menu_id, + parent_id, + menu_name, + order_num, + path, + component, + `query`, + is_frame, + is_cache, + menu_type, + visible, + status, + perms, + icon, + remark, + create_by, + create_time + )values( + #{menuId}, + #{parentId}, + #{menuName}, + #{orderNum}, + #{path}, + #{component}, + #{query}, + #{isFrame}, + #{isCache}, + #{menuType}, + #{visible}, + #{status}, + #{perms}, + #{icon}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + delete from sys_menu where menu_id = #{menuId} + + + diff --git a/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml new file mode 100644 index 00000000..65d30794 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + select notice_id, notice_title, notice_type, cast(notice_content as char) as notice_content, status, create_by, create_time, update_by, update_time, remark + from sys_notice + + + + + + + + insert into sys_notice ( + notice_title, + notice_type, + notice_content, + status, + remark, + create_by, + create_time + )values( + #{noticeTitle}, + #{noticeType}, + #{noticeContent}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + update sys_notice + + notice_title = #{noticeTitle}, + notice_type = #{noticeType}, + notice_content = #{noticeContent}, + status = #{status}, + update_by = #{updateBy}, + update_time = sysdate() + + where notice_id = #{noticeId} + + + + delete from sys_notice where notice_id = #{noticeId} + + + + delete from sys_notice where notice_id in + + #{noticeId} + + + + \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysOperLogMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysOperLogMapper.xml new file mode 100644 index 00000000..018a7471 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysOperLogMapper.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + select oper_id, title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_location, oper_param, json_result, status, error_msg, oper_time + from sys_oper_log + + + + insert into sys_oper_log(title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_location, oper_param, json_result, status, error_msg, oper_time) + values (#{title}, #{businessType}, #{method}, #{requestMethod}, #{operatorType}, #{operName}, #{deptName}, #{operUrl}, #{operIp}, #{operLocation}, #{operParam}, #{jsonResult}, #{status}, #{errorMsg}, sysdate()) + + + + + + delete from sys_oper_log where oper_id in + + #{operId} + + + + + + + truncate table sys_oper_log + + + \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml new file mode 100644 index 00000000..fc37f49d --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + select post_id, post_code, post_name, post_sort, status, create_by, create_time, remark + from sys_post + + + + + + + + + + + + + + + + + + update sys_post + + post_code = #{postCode}, + post_name = #{postName}, + post_sort = #{postSort}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where post_id = #{postId} + + + + insert into sys_post( + post_id, + post_code, + post_name, + post_sort, + status, + remark, + create_by, + create_time + )values( + #{postId}, + #{postCode}, + #{postName}, + #{postSort}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + delete from sys_post where post_id = #{postId} + + + + delete from sys_post where post_id in + + #{postId} + + + + \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml new file mode 100644 index 00000000..7c4139bc --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + delete from sys_role_dept where role_id=#{roleId} + + + + + + delete from sys_role_dept where role_id in + + #{roleId} + + + + + insert into sys_role_dept(role_id, dept_id) values + + (#{item.roleId},#{item.deptId}) + + + + \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml new file mode 100644 index 00000000..58743901 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + select distinct r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.menu_check_strictly, r.dept_check_strictly, + r.status, r.del_flag, r.create_time, r.remark + from sys_role r + left join sys_user_role urs on urs.role_id = r.role_id + left join sys_user u on u.user_id = urs.user_id + left join sys_dept d on u.dept_id = d.dept_id + + + + + + + + + + + + + + + + + + + + insert into sys_role( + role_id, + role_name, + role_key, + role_sort, + data_scope, + menu_check_strictly, + dept_check_strictly, + status, + remark, + create_by, + create_time + )values( + #{roleId}, + #{roleName}, + #{roleKey}, + #{roleSort}, + #{dataScope}, + #{menuCheckStrictly}, + #{deptCheckStrictly}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + update sys_role + + role_name = #{roleName}, + role_key = #{roleKey}, + role_sort = #{roleSort}, + data_scope = #{dataScope}, + menu_check_strictly = #{menuCheckStrictly}, + dept_check_strictly = #{deptCheckStrictly}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where role_id = #{roleId} + + + + update sys_role set del_flag = '2' where role_id = #{roleId} + + + + update sys_role set del_flag = '2' where role_id in + + #{roleId} + + + + diff --git a/ruoyi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml new file mode 100644 index 00000000..cb60a852 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + delete from sys_role_menu where role_id=#{roleId} + + + + delete from sys_role_menu where role_id in + + #{roleId} + + + + + insert into sys_role_menu(role_id, menu_id) values + + (#{item.roleId},#{item.menuId}) + + + + \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysTreeDictDataMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysTreeDictDataMapper.xml new file mode 100644 index 00000000..69421d5b --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysTreeDictDataMapper.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + SELECT + o.id as id, + o.label as label, + o.tree_dict as tree_dict, + o.code as code, + o.remark as remark, + o.pid as pid, + o.order_num as order_num, + o.level_code as level_code, + o.level_depth as level_depth, + o.is_leaf as is_leaf, + o.path as path, + o.icon as icon + FROM sys_tree_dict_data o + LEFT JOIN sys_user u ON u.user_id = o.create_by + LEFT JOIN sys_dept d ON u.dept_id = d.dept_id + + + + + + diff --git a/ruoyi-system/src/main/resources/mapper/system/SysTreeDictMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysTreeDictMapper.xml new file mode 100644 index 00000000..f274a9a0 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysTreeDictMapper.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + SELECT + o.id as id, + o.code as code, + o.name as name, + o.stru_type as stru_type, + o.create_by as create_by, + o.remark as remark, + o.is_sys_param as is_sys_param + FROM sys_tree_dict o + + + + + + diff --git a/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml new file mode 100644 index 00000000..1c170fd5 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml @@ -0,0 +1,232 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark,u.tenant_id, + d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.status as dept_status, + r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status as role_status + from sys_user u + left join sys_dept d on u.dept_id = d.dept_id + left join sys_user_role urs on u.user_id = urs.user_id + left join sys_role r on r.role_id = urs.role_id + + + + + + + + + + + + + + + + + + + + insert into sys_user( + user_id, + dept_id, + user_name, + nick_name, + email, + avatar, + phonenumber, + sex, + password, + status, + create_by, + remark, + create_time + )values( + #{userId}, + #{deptId}, + #{userName}, + #{nickName}, + #{email}, + #{avatar}, + #{phonenumber}, + #{sex}, + #{password}, + #{status}, + #{createBy}, + #{remark}, + sysdate() + ) + + + + update sys_user + + dept_id = #{deptId}, + user_name = #{userName}, + nick_name = #{nickName}, + email = #{email}, + phonenumber = #{phonenumber}, + sex = #{sex}, + avatar = #{avatar}, + password = #{password}, + status = #{status}, + login_ip = #{loginIp}, + login_date = #{loginDate}, + update_by = #{updateBy}, + remark = #{remark}, + update_time = sysdate() + + where user_id = #{userId} + + + + update sys_user set status = #{status} where user_id = #{userId} + + + + update sys_user set avatar = #{avatar} where user_name = #{userName} + + + + update sys_user set password = #{password} where user_name = #{userName} + + + + update sys_user set del_flag = '2' where user_id = #{userId} + + + + update sys_user set del_flag = '2' where user_id in + + #{userId} + + + + diff --git a/ruoyi-system/src/main/resources/mapper/system/SysUserPostMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysUserPostMapper.xml new file mode 100644 index 00000000..912f9dfb --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysUserPostMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + delete from sys_user_post where user_id=#{userId} + + + + + + delete from sys_user_post where user_id in + + #{userId} + + + + + insert into sys_user_post(user_id, post_id) values + + (#{item.userId},#{item.postId}) + + + + diff --git a/ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml new file mode 100644 index 00000000..dd726891 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + delete from sys_user_role where user_id=#{userId} + + + + + + delete from sys_user_role where user_id in + + #{userId} + + + + + insert into sys_user_role(user_id, role_id) values + + (#{item.userId},#{item.roleId}) + + + + + delete from sys_user_role where user_id=#{userId} and role_id=#{roleId} + + + + delete from sys_user_role where role_id=#{roleId} and user_id in + + #{userId} + + + \ No newline at end of file diff --git a/ruoyi-system/target/classes/com/ruoyi/system/convert/Device/DeviceConvert.class b/ruoyi-system/target/classes/com/ruoyi/system/convert/Device/DeviceConvert.class new file mode 100644 index 00000000..14e6af1c Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/convert/Device/DeviceConvert.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/convert/Device/DeviceConvertImpl.class b/ruoyi-system/target/classes/com/ruoyi/system/convert/Device/DeviceConvertImpl.class new file mode 100644 index 00000000..e0c1c267 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/convert/Device/DeviceConvertImpl.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/convert/task/TaskConvert.class b/ruoyi-system/target/classes/com/ruoyi/system/convert/task/TaskConvert.class new file mode 100644 index 00000000..0ecbe4e7 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/convert/task/TaskConvert.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/convert/task/TaskConvertImpl.class b/ruoyi-system/target/classes/com/ruoyi/system/convert/task/TaskConvertImpl.class new file mode 100644 index 00000000..61cf2cb1 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/convert/task/TaskConvertImpl.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/convert/tenant/TenantConvert.class b/ruoyi-system/target/classes/com/ruoyi/system/convert/tenant/TenantConvert.class new file mode 100644 index 00000000..71307d17 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/convert/tenant/TenantConvert.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/convert/tenant/TenantConvertImpl.class b/ruoyi-system/target/classes/com/ruoyi/system/convert/tenant/TenantConvertImpl.class new file mode 100644 index 00000000..8aa839bc Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/convert/tenant/TenantConvertImpl.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/convert/tenant/TenantPackageConvert.class b/ruoyi-system/target/classes/com/ruoyi/system/convert/tenant/TenantPackageConvert.class new file mode 100644 index 00000000..04d09475 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/convert/tenant/TenantPackageConvert.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/convert/tenant/TenantPackageConvertImpl.class b/ruoyi-system/target/classes/com/ruoyi/system/convert/tenant/TenantPackageConvertImpl.class new file mode 100644 index 00000000..bfa56e2b Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/convert/tenant/TenantPackageConvertImpl.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/convert/user/UserConvert.class b/ruoyi-system/target/classes/com/ruoyi/system/convert/user/UserConvert.class new file mode 100644 index 00000000..3ec93aca Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/convert/user/UserConvert.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/convert/user/UserConvertImpl.class b/ruoyi-system/target/classes/com/ruoyi/system/convert/user/UserConvertImpl.class new file mode 100644 index 00000000..3166f5d9 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/convert/user/UserConvertImpl.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/SysCache.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/SysCache.class new file mode 100644 index 00000000..946cb645 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/SysCache.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/SysConfig.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/SysConfig.class new file mode 100644 index 00000000..21e01451 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/SysConfig.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/SysLogininfor.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/SysLogininfor.class new file mode 100644 index 00000000..5a550b25 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/SysLogininfor.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/SysNotice.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/SysNotice.class new file mode 100644 index 00000000..b19e6afd Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/SysNotice.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/SysOperLog.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/SysOperLog.class new file mode 100644 index 00000000..2b327b08 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/SysOperLog.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/SysPost.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/SysPost.class new file mode 100644 index 00000000..7b5f8191 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/SysPost.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/SysRoleDept.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/SysRoleDept.class new file mode 100644 index 00000000..0479a665 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/SysRoleDept.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/SysRoleMenu.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/SysRoleMenu.class new file mode 100644 index 00000000..1c0c26bf Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/SysRoleMenu.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/SysTreeDict.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/SysTreeDict.class new file mode 100644 index 00000000..2610d78f Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/SysTreeDict.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/SysTreeDictData.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/SysTreeDictData.class new file mode 100644 index 00000000..b5ad35d3 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/SysTreeDictData.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/SysUserOnline.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/SysUserOnline.class new file mode 100644 index 00000000..70aef709 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/SysUserOnline.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/SysUserPost.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/SysUserPost.class new file mode 100644 index 00000000..39ec47ee Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/SysUserPost.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/SysUserRole.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/SysUserRole.class new file mode 100644 index 00000000..2d0e3245 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/SysUserRole.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/TenantDO$TenantDOBuilder.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/TenantDO$TenantDOBuilder.class new file mode 100644 index 00000000..1b556bf0 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/TenantDO$TenantDOBuilder.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/TenantDO.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/TenantDO.class new file mode 100644 index 00000000..d4f57d8d Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/TenantDO.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/TenantPackageDO$TenantPackageDOBuilder.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/TenantPackageDO$TenantPackageDOBuilder.class new file mode 100644 index 00000000..1163d3fb Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/TenantPackageDO$TenantPackageDOBuilder.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/TenantPackageDO.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/TenantPackageDO.class new file mode 100644 index 00000000..1b23fb2c Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/TenantPackageDO.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/ImageVo.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/ImageVo.class new file mode 100644 index 00000000..5849ad9d Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/ImageVo.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/MetaVo.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/MetaVo.class new file mode 100644 index 00000000..43975f53 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/MetaVo.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/NameIdVo.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/NameIdVo.class new file mode 100644 index 00000000..f1dd488c Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/NameIdVo.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/RouterVo.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/RouterVo.class new file mode 100644 index 00000000..190e8f93 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/RouterVo.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/UserInfoVo.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/UserInfoVo.class new file mode 100644 index 00000000..34408304 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/UserInfoVo.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/menu/MenuSimpleRespVO.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/menu/MenuSimpleRespVO.class new file mode 100644 index 00000000..6cfef50a Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/menu/MenuSimpleRespVO.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/packages/TenantPackageBaseVO.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/packages/TenantPackageBaseVO.class new file mode 100644 index 00000000..a0f6d99b Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/packages/TenantPackageBaseVO.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/packages/TenantPackageCreateReqVO.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/packages/TenantPackageCreateReqVO.class new file mode 100644 index 00000000..ecff5565 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/packages/TenantPackageCreateReqVO.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/packages/TenantPackagePageReqVO.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/packages/TenantPackagePageReqVO.class new file mode 100644 index 00000000..d7927cdc Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/packages/TenantPackagePageReqVO.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/packages/TenantPackageRespVO.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/packages/TenantPackageRespVO.class new file mode 100644 index 00000000..9a2067c9 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/packages/TenantPackageRespVO.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/packages/TenantPackageSimpleRespVO.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/packages/TenantPackageSimpleRespVO.class new file mode 100644 index 00000000..7a1c4bfc Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/packages/TenantPackageSimpleRespVO.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/packages/TenantPackageUpdateReqVO.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/packages/TenantPackageUpdateReqVO.class new file mode 100644 index 00000000..3eadcd3c Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/packages/TenantPackageUpdateReqVO.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/report/ReportDataRequestVo.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/report/ReportDataRequestVo.class new file mode 100644 index 00000000..522532ce Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/report/ReportDataRequestVo.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/report/ReportDataResponseVo.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/report/ReportDataResponseVo.class new file mode 100644 index 00000000..227ff57a Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/report/ReportDataResponseVo.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/report/ReportDownLoadVo.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/report/ReportDownLoadVo.class new file mode 100644 index 00000000..12677625 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/report/ReportDownLoadVo.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/task/BatchSettingResqVo.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/task/BatchSettingResqVo.class new file mode 100644 index 00000000..099617e4 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/task/BatchSettingResqVo.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/task/PointResqVo.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/task/PointResqVo.class new file mode 100644 index 00000000..81b9e25d Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/task/PointResqVo.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/task/SettingUserInfoResqVo.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/task/SettingUserInfoResqVo.class new file mode 100644 index 00000000..f67f5a5c Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/task/SettingUserInfoResqVo.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/tenant/TenantBaseVO.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/tenant/TenantBaseVO.class new file mode 100644 index 00000000..18ff1f90 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/tenant/TenantBaseVO.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/tenant/TenantCreateReqVO.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/tenant/TenantCreateReqVO.class new file mode 100644 index 00000000..ad3dd49e Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/tenant/TenantCreateReqVO.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/tenant/TenantExcelVO.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/tenant/TenantExcelVO.class new file mode 100644 index 00000000..89565b11 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/tenant/TenantExcelVO.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/tenant/TenantExportReqVO.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/tenant/TenantExportReqVO.class new file mode 100644 index 00000000..4aee7538 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/tenant/TenantExportReqVO.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/tenant/TenantPageReqVO.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/tenant/TenantPageReqVO.class new file mode 100644 index 00000000..c96dc77e Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/tenant/TenantPageReqVO.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/tenant/TenantRespVO.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/tenant/TenantRespVO.class new file mode 100644 index 00000000..07d404ac Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/tenant/TenantRespVO.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/tenant/TenantUpdateReqVO.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/tenant/TenantUpdateReqVO.class new file mode 100644 index 00000000..44bf1225 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/tenant/TenantUpdateReqVO.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/user/UserSimpleRespVO.class b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/user/UserSimpleRespVO.class new file mode 100644 index 00000000..7998f391 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/user/UserSimpleRespVO.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/enums/ReportTypeEnum.class b/ruoyi-system/target/classes/com/ruoyi/system/enums/ReportTypeEnum.class new file mode 100644 index 00000000..7bedddee Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/enums/ReportTypeEnum.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysConfigMapper.class b/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysConfigMapper.class new file mode 100644 index 00000000..7553bcc4 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysConfigMapper.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysDeptMapper.class b/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysDeptMapper.class new file mode 100644 index 00000000..5f80a76c Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysDeptMapper.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysDictDataMapper.class b/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysDictDataMapper.class new file mode 100644 index 00000000..51886cdf Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysDictDataMapper.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysDictTypeMapper.class b/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysDictTypeMapper.class new file mode 100644 index 00000000..1ba177f8 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysDictTypeMapper.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysLogininforMapper.class b/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysLogininforMapper.class new file mode 100644 index 00000000..2bd4190a Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysLogininforMapper.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysMenuMapper.class b/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysMenuMapper.class new file mode 100644 index 00000000..5dd74ac5 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysMenuMapper.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysNoticeMapper.class b/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysNoticeMapper.class new file mode 100644 index 00000000..8d098b34 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysNoticeMapper.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysOperLogMapper.class b/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysOperLogMapper.class new file mode 100644 index 00000000..6c35a1c7 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysOperLogMapper.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysPostMapper.class b/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysPostMapper.class new file mode 100644 index 00000000..083c6168 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysPostMapper.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysRoleDeptMapper.class b/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysRoleDeptMapper.class new file mode 100644 index 00000000..e9fed737 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysRoleDeptMapper.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysRoleMapper.class b/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysRoleMapper.class new file mode 100644 index 00000000..7eae59a5 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysRoleMapper.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysRoleMenuMapper.class b/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysRoleMenuMapper.class new file mode 100644 index 00000000..9fa86135 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysRoleMenuMapper.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysTreeDictDataMapper.class b/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysTreeDictDataMapper.class new file mode 100644 index 00000000..ec9dd1ca Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysTreeDictDataMapper.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysTreeDictMapper.class b/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysTreeDictMapper.class new file mode 100644 index 00000000..5a50246e Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysTreeDictMapper.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysUserMapper.class b/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysUserMapper.class new file mode 100644 index 00000000..781ad597 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysUserMapper.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysUserPostMapper.class b/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysUserPostMapper.class new file mode 100644 index 00000000..006dd034 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysUserPostMapper.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysUserRoleMapper.class b/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysUserRoleMapper.class new file mode 100644 index 00000000..e2d70868 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysUserRoleMapper.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/mapper/TenantMapper.class b/ruoyi-system/target/classes/com/ruoyi/system/mapper/TenantMapper.class new file mode 100644 index 00000000..a881d003 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/mapper/TenantMapper.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/mapper/TenantPackageMapper.class b/ruoyi-system/target/classes/com/ruoyi/system/mapper/TenantPackageMapper.class new file mode 100644 index 00000000..8494136a Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/mapper/TenantPackageMapper.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/service/ISysConfigService.class b/ruoyi-system/target/classes/com/ruoyi/system/service/ISysConfigService.class new file mode 100644 index 00000000..81252ab7 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/service/ISysConfigService.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/service/ISysDeptService.class b/ruoyi-system/target/classes/com/ruoyi/system/service/ISysDeptService.class new file mode 100644 index 00000000..562eb32c Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/service/ISysDeptService.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/service/ISysDictDataService.class b/ruoyi-system/target/classes/com/ruoyi/system/service/ISysDictDataService.class new file mode 100644 index 00000000..ebb09fff Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/service/ISysDictDataService.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/service/ISysDictTypeService.class b/ruoyi-system/target/classes/com/ruoyi/system/service/ISysDictTypeService.class new file mode 100644 index 00000000..b8ff632c Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/service/ISysDictTypeService.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/service/ISysLogininforService.class b/ruoyi-system/target/classes/com/ruoyi/system/service/ISysLogininforService.class new file mode 100644 index 00000000..1005acda Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/service/ISysLogininforService.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/service/ISysMenuService.class b/ruoyi-system/target/classes/com/ruoyi/system/service/ISysMenuService.class new file mode 100644 index 00000000..45f5528b Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/service/ISysMenuService.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/service/ISysNoticeService.class b/ruoyi-system/target/classes/com/ruoyi/system/service/ISysNoticeService.class new file mode 100644 index 00000000..09a84e97 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/service/ISysNoticeService.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/service/ISysOperLogService.class b/ruoyi-system/target/classes/com/ruoyi/system/service/ISysOperLogService.class new file mode 100644 index 00000000..112bf28f Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/service/ISysOperLogService.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/service/ISysPostService.class b/ruoyi-system/target/classes/com/ruoyi/system/service/ISysPostService.class new file mode 100644 index 00000000..d2782ae3 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/service/ISysPostService.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/service/ISysRoleService.class b/ruoyi-system/target/classes/com/ruoyi/system/service/ISysRoleService.class new file mode 100644 index 00000000..289c3a74 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/service/ISysRoleService.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/service/ISysTreeDictDataService.class b/ruoyi-system/target/classes/com/ruoyi/system/service/ISysTreeDictDataService.class new file mode 100644 index 00000000..c5c18c78 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/service/ISysTreeDictDataService.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/service/ISysTreeDictService.class b/ruoyi-system/target/classes/com/ruoyi/system/service/ISysTreeDictService.class new file mode 100644 index 00000000..54da39f0 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/service/ISysTreeDictService.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/service/ISysUserOnlineService.class b/ruoyi-system/target/classes/com/ruoyi/system/service/ISysUserOnlineService.class new file mode 100644 index 00000000..160f48d4 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/service/ISysUserOnlineService.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/service/ISysUserService.class b/ruoyi-system/target/classes/com/ruoyi/system/service/ISysUserService.class new file mode 100644 index 00000000..ab377d67 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/service/ISysUserService.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/service/TenantPackageService.class b/ruoyi-system/target/classes/com/ruoyi/system/service/TenantPackageService.class new file mode 100644 index 00000000..ebd75687 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/service/TenantPackageService.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/service/TenantService.class b/ruoyi-system/target/classes/com/ruoyi/system/service/TenantService.class new file mode 100644 index 00000000..a2be9738 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/service/TenantService.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/service/handler/TenantInfoHandler.class b/ruoyi-system/target/classes/com/ruoyi/system/service/handler/TenantInfoHandler.class new file mode 100644 index 00000000..3028f891 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/service/handler/TenantInfoHandler.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/service/handler/TenantMenuHandler.class b/ruoyi-system/target/classes/com/ruoyi/system/service/handler/TenantMenuHandler.class new file mode 100644 index 00000000..1d00f8a6 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/service/handler/TenantMenuHandler.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysConfigServiceImpl.class b/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysConfigServiceImpl.class new file mode 100644 index 00000000..15ca3a3b Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysConfigServiceImpl.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysDeptServiceImpl.class b/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysDeptServiceImpl.class new file mode 100644 index 00000000..d6b43f7e Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysDeptServiceImpl.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysDictDataServiceImpl.class b/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysDictDataServiceImpl.class new file mode 100644 index 00000000..e9113c51 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysDictDataServiceImpl.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.class b/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.class new file mode 100644 index 00000000..dd5034c7 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysLogininforServiceImpl.class b/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysLogininforServiceImpl.class new file mode 100644 index 00000000..5d91ad1c Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysLogininforServiceImpl.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysMenuServiceImpl.class b/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysMenuServiceImpl.class new file mode 100644 index 00000000..e7ae8cec Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysMenuServiceImpl.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysNoticeServiceImpl.class b/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysNoticeServiceImpl.class new file mode 100644 index 00000000..efeeb092 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysNoticeServiceImpl.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysOperLogServiceImpl.class b/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysOperLogServiceImpl.class new file mode 100644 index 00000000..03f85439 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysOperLogServiceImpl.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysPostServiceImpl.class b/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysPostServiceImpl.class new file mode 100644 index 00000000..cd715881 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysPostServiceImpl.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysRoleServiceImpl.class b/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysRoleServiceImpl.class new file mode 100644 index 00000000..f9066776 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysRoleServiceImpl.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.class b/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.class new file mode 100644 index 00000000..cdf53b04 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysUserServiceImpl.class b/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysUserServiceImpl.class new file mode 100644 index 00000000..fd5ae49e Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysUserServiceImpl.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/service/impl/TenantPackageServiceImpl.class b/ruoyi-system/target/classes/com/ruoyi/system/service/impl/TenantPackageServiceImpl.class new file mode 100644 index 00000000..d6e15e56 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/service/impl/TenantPackageServiceImpl.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/service/impl/TenantServiceImpl.class b/ruoyi-system/target/classes/com/ruoyi/system/service/impl/TenantServiceImpl.class new file mode 100644 index 00000000..2c603018 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/service/impl/TenantServiceImpl.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/service/impl/TreeDictDataServiceImpl.class b/ruoyi-system/target/classes/com/ruoyi/system/service/impl/TreeDictDataServiceImpl.class new file mode 100644 index 00000000..f00565db Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/service/impl/TreeDictDataServiceImpl.class differ diff --git a/ruoyi-system/target/classes/com/ruoyi/system/service/impl/TreeDictServiceImpl.class b/ruoyi-system/target/classes/com/ruoyi/system/service/impl/TreeDictServiceImpl.class new file mode 100644 index 00000000..09344918 Binary files /dev/null and b/ruoyi-system/target/classes/com/ruoyi/system/service/impl/TreeDictServiceImpl.class differ diff --git a/ruoyi-system/target/classes/mapper/system/SysConfigMapper.xml b/ruoyi-system/target/classes/mapper/system/SysConfigMapper.xml new file mode 100644 index 00000000..8b979061 --- /dev/null +++ b/ruoyi-system/target/classes/mapper/system/SysConfigMapper.xml @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + select config_id, config_name, config_key, config_value, config_type, create_by, create_time, update_by, update_time, remark + from sys_config + + + + + + + and config_id = #{configId} + + + and config_key = #{configKey} + + + + + + + + + + + + insert into sys_config ( + config_name, + config_key, + config_value, + config_type, + create_by, + remark, + create_time + )values( + #{configName}, + #{configKey}, + #{configValue}, + #{configType}, + #{createBy}, + #{remark}, + sysdate() + ) + + + + update sys_config + + config_name = #{configName}, + config_key = #{configKey}, + config_value = #{configValue}, + config_type = #{configType}, + update_by = #{updateBy}, + remark = #{remark}, + update_time = sysdate() + + where config_id = #{configId} + + + + delete from sys_config where config_id = #{configId} + + + + delete from sys_config where config_id in + + #{configId} + + + + \ No newline at end of file diff --git a/ruoyi-system/target/classes/mapper/system/SysDeptMapper.xml b/ruoyi-system/target/classes/mapper/system/SysDeptMapper.xml new file mode 100644 index 00000000..1786c907 --- /dev/null +++ b/ruoyi-system/target/classes/mapper/system/SysDeptMapper.xml @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + select d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.phone, d.email, d.status, d.del_flag, d.create_by, d.create_time + from sys_dept d + + + + + + + + + + + + + + + + + + + + insert into sys_dept( + dept_id, + parent_id, + dept_name, + ancestors, + order_num, + leader, + phone, + email, + status, + create_by, + create_time + )values( + #{deptId}, + #{parentId}, + #{deptName}, + #{ancestors}, + #{orderNum}, + #{leader}, + #{phone}, + #{email}, + #{status}, + #{createBy}, + sysdate() + ) + + + + update sys_dept + + parent_id = #{parentId}, + dept_name = #{deptName}, + ancestors = #{ancestors}, + order_num = #{orderNum}, + leader = #{leader}, + phone = #{phone}, + email = #{email}, + status = #{status}, + update_by = #{updateBy}, + update_time = sysdate() + + where dept_id = #{deptId} + + + + update sys_dept set ancestors = + + when #{item.deptId} then #{item.ancestors} + + where dept_id in + + #{item.deptId} + + + + + update sys_dept set status = '0' where dept_id in + + #{deptId} + + + + + update sys_dept set del_flag = '2' where dept_id = #{deptId} + + + diff --git a/ruoyi-system/target/classes/mapper/system/SysDictDataMapper.xml b/ruoyi-system/target/classes/mapper/system/SysDictDataMapper.xml new file mode 100644 index 00000000..8da9030b --- /dev/null +++ b/ruoyi-system/target/classes/mapper/system/SysDictDataMapper.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + select dict_code, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark + from sys_dict_data + + + + + + + + + + + + + + delete from sys_dict_data where dict_code = #{dictCode} + + + + delete from sys_dict_data where dict_code in + + #{dictCode} + + + + + update sys_dict_data + + dict_sort = #{dictSort}, + dict_label = #{dictLabel}, + dict_value = #{dictValue}, + dict_type = #{dictType}, + css_class = #{cssClass}, + list_class = #{listClass}, + is_default = #{isDefault}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where dict_code = #{dictCode} + + + + update sys_dict_data set dict_type = #{newDictType} where dict_type = #{oldDictType} + + + + insert into sys_dict_data( + dict_sort, + dict_label, + dict_value, + dict_type, + css_class, + list_class, + is_default, + status, + remark, + create_by, + create_time + )values( + #{dictSort}, + #{dictLabel}, + #{dictValue}, + #{dictType}, + #{cssClass}, + #{listClass}, + #{isDefault}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + \ No newline at end of file diff --git a/ruoyi-system/target/classes/mapper/system/SysDictTypeMapper.xml b/ruoyi-system/target/classes/mapper/system/SysDictTypeMapper.xml new file mode 100644 index 00000000..55b4075f --- /dev/null +++ b/ruoyi-system/target/classes/mapper/system/SysDictTypeMapper.xml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + select dict_id, dict_name, dict_type, status, create_by, create_time, remark + from sys_dict_type + + + + + + + + + + + + + + delete from sys_dict_type where dict_id = #{dictId} + + + + delete from sys_dict_type where dict_id in + + #{dictId} + + + + + update sys_dict_type + + dict_name = #{dictName}, + dict_type = #{dictType}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where dict_id = #{dictId} + + + + insert into sys_dict_type( + dict_name, + dict_type, + status, + remark, + create_by, + create_time + )values( + #{dictName}, + #{dictType}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + \ No newline at end of file diff --git a/ruoyi-system/target/classes/mapper/system/SysLogininforMapper.xml b/ruoyi-system/target/classes/mapper/system/SysLogininforMapper.xml new file mode 100644 index 00000000..b8178fa3 --- /dev/null +++ b/ruoyi-system/target/classes/mapper/system/SysLogininforMapper.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + insert into sys_logininfor (user_name, status, ipaddr, login_location, browser, os, msg, login_time) + values (#{userName}, #{status}, #{ipaddr}, #{loginLocation}, #{browser}, #{os}, #{msg}, sysdate()) + + + + + + delete from sys_logininfor where info_id in + + #{infoId} + + + + + truncate table sys_logininfor + + + \ No newline at end of file diff --git a/ruoyi-system/target/classes/mapper/system/SysMenuMapper.xml b/ruoyi-system/target/classes/mapper/system/SysMenuMapper.xml new file mode 100644 index 00000000..eb995204 --- /dev/null +++ b/ruoyi-system/target/classes/mapper/system/SysMenuMapper.xml @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + select menu_id, menu_name, parent_id, order_num, path, component, `query`, is_frame, is_cache, menu_type, visible, status, ifnull(perms,'') as perms, icon, create_time + from sys_menu + + + + + + + + + + + + + + + + + + + + + + + + + + update sys_menu + + menu_name = #{menuName}, + parent_id = #{parentId}, + order_num = #{orderNum}, + path = #{path}, + component = #{component}, + `query` = #{query}, + is_frame = #{isFrame}, + is_cache = #{isCache}, + menu_type = #{menuType}, + visible = #{visible}, + status = #{status}, + perms = #{perms}, + icon = #{icon}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where menu_id = #{menuId} + + + + insert into sys_menu( + menu_id, + parent_id, + menu_name, + order_num, + path, + component, + `query`, + is_frame, + is_cache, + menu_type, + visible, + status, + perms, + icon, + remark, + create_by, + create_time + )values( + #{menuId}, + #{parentId}, + #{menuName}, + #{orderNum}, + #{path}, + #{component}, + #{query}, + #{isFrame}, + #{isCache}, + #{menuType}, + #{visible}, + #{status}, + #{perms}, + #{icon}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + delete from sys_menu where menu_id = #{menuId} + + + diff --git a/ruoyi-system/target/classes/mapper/system/SysNoticeMapper.xml b/ruoyi-system/target/classes/mapper/system/SysNoticeMapper.xml new file mode 100644 index 00000000..65d30794 --- /dev/null +++ b/ruoyi-system/target/classes/mapper/system/SysNoticeMapper.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + select notice_id, notice_title, notice_type, cast(notice_content as char) as notice_content, status, create_by, create_time, update_by, update_time, remark + from sys_notice + + + + + + + + insert into sys_notice ( + notice_title, + notice_type, + notice_content, + status, + remark, + create_by, + create_time + )values( + #{noticeTitle}, + #{noticeType}, + #{noticeContent}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + update sys_notice + + notice_title = #{noticeTitle}, + notice_type = #{noticeType}, + notice_content = #{noticeContent}, + status = #{status}, + update_by = #{updateBy}, + update_time = sysdate() + + where notice_id = #{noticeId} + + + + delete from sys_notice where notice_id = #{noticeId} + + + + delete from sys_notice where notice_id in + + #{noticeId} + + + + \ No newline at end of file diff --git a/ruoyi-system/target/classes/mapper/system/SysOperLogMapper.xml b/ruoyi-system/target/classes/mapper/system/SysOperLogMapper.xml new file mode 100644 index 00000000..018a7471 --- /dev/null +++ b/ruoyi-system/target/classes/mapper/system/SysOperLogMapper.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + select oper_id, title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_location, oper_param, json_result, status, error_msg, oper_time + from sys_oper_log + + + + insert into sys_oper_log(title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_location, oper_param, json_result, status, error_msg, oper_time) + values (#{title}, #{businessType}, #{method}, #{requestMethod}, #{operatorType}, #{operName}, #{deptName}, #{operUrl}, #{operIp}, #{operLocation}, #{operParam}, #{jsonResult}, #{status}, #{errorMsg}, sysdate()) + + + + + + delete from sys_oper_log where oper_id in + + #{operId} + + + + + + + truncate table sys_oper_log + + + \ No newline at end of file diff --git a/ruoyi-system/target/classes/mapper/system/SysPostMapper.xml b/ruoyi-system/target/classes/mapper/system/SysPostMapper.xml new file mode 100644 index 00000000..fc37f49d --- /dev/null +++ b/ruoyi-system/target/classes/mapper/system/SysPostMapper.xml @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + select post_id, post_code, post_name, post_sort, status, create_by, create_time, remark + from sys_post + + + + + + + + + + + + + + + + + + update sys_post + + post_code = #{postCode}, + post_name = #{postName}, + post_sort = #{postSort}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where post_id = #{postId} + + + + insert into sys_post( + post_id, + post_code, + post_name, + post_sort, + status, + remark, + create_by, + create_time + )values( + #{postId}, + #{postCode}, + #{postName}, + #{postSort}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + delete from sys_post where post_id = #{postId} + + + + delete from sys_post where post_id in + + #{postId} + + + + \ No newline at end of file diff --git a/ruoyi-system/target/classes/mapper/system/SysRoleDeptMapper.xml b/ruoyi-system/target/classes/mapper/system/SysRoleDeptMapper.xml new file mode 100644 index 00000000..7c4139bc --- /dev/null +++ b/ruoyi-system/target/classes/mapper/system/SysRoleDeptMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + delete from sys_role_dept where role_id=#{roleId} + + + + + + delete from sys_role_dept where role_id in + + #{roleId} + + + + + insert into sys_role_dept(role_id, dept_id) values + + (#{item.roleId},#{item.deptId}) + + + + \ No newline at end of file diff --git a/ruoyi-system/target/classes/mapper/system/SysRoleMapper.xml b/ruoyi-system/target/classes/mapper/system/SysRoleMapper.xml new file mode 100644 index 00000000..58743901 --- /dev/null +++ b/ruoyi-system/target/classes/mapper/system/SysRoleMapper.xml @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + select distinct r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.menu_check_strictly, r.dept_check_strictly, + r.status, r.del_flag, r.create_time, r.remark + from sys_role r + left join sys_user_role urs on urs.role_id = r.role_id + left join sys_user u on u.user_id = urs.user_id + left join sys_dept d on u.dept_id = d.dept_id + + + + + + + + + + + + + + + + + + + + insert into sys_role( + role_id, + role_name, + role_key, + role_sort, + data_scope, + menu_check_strictly, + dept_check_strictly, + status, + remark, + create_by, + create_time + )values( + #{roleId}, + #{roleName}, + #{roleKey}, + #{roleSort}, + #{dataScope}, + #{menuCheckStrictly}, + #{deptCheckStrictly}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + update sys_role + + role_name = #{roleName}, + role_key = #{roleKey}, + role_sort = #{roleSort}, + data_scope = #{dataScope}, + menu_check_strictly = #{menuCheckStrictly}, + dept_check_strictly = #{deptCheckStrictly}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where role_id = #{roleId} + + + + update sys_role set del_flag = '2' where role_id = #{roleId} + + + + update sys_role set del_flag = '2' where role_id in + + #{roleId} + + + + diff --git a/ruoyi-system/target/classes/mapper/system/SysRoleMenuMapper.xml b/ruoyi-system/target/classes/mapper/system/SysRoleMenuMapper.xml new file mode 100644 index 00000000..cb60a852 --- /dev/null +++ b/ruoyi-system/target/classes/mapper/system/SysRoleMenuMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + delete from sys_role_menu where role_id=#{roleId} + + + + delete from sys_role_menu where role_id in + + #{roleId} + + + + + insert into sys_role_menu(role_id, menu_id) values + + (#{item.roleId},#{item.menuId}) + + + + \ No newline at end of file diff --git a/ruoyi-system/target/classes/mapper/system/SysTreeDictDataMapper.xml b/ruoyi-system/target/classes/mapper/system/SysTreeDictDataMapper.xml new file mode 100644 index 00000000..69421d5b --- /dev/null +++ b/ruoyi-system/target/classes/mapper/system/SysTreeDictDataMapper.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + SELECT + o.id as id, + o.label as label, + o.tree_dict as tree_dict, + o.code as code, + o.remark as remark, + o.pid as pid, + o.order_num as order_num, + o.level_code as level_code, + o.level_depth as level_depth, + o.is_leaf as is_leaf, + o.path as path, + o.icon as icon + FROM sys_tree_dict_data o + LEFT JOIN sys_user u ON u.user_id = o.create_by + LEFT JOIN sys_dept d ON u.dept_id = d.dept_id + + + + + + diff --git a/ruoyi-system/target/classes/mapper/system/SysTreeDictMapper.xml b/ruoyi-system/target/classes/mapper/system/SysTreeDictMapper.xml new file mode 100644 index 00000000..f274a9a0 --- /dev/null +++ b/ruoyi-system/target/classes/mapper/system/SysTreeDictMapper.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + SELECT + o.id as id, + o.code as code, + o.name as name, + o.stru_type as stru_type, + o.create_by as create_by, + o.remark as remark, + o.is_sys_param as is_sys_param + FROM sys_tree_dict o + + + + + + diff --git a/ruoyi-system/target/classes/mapper/system/SysUserMapper.xml b/ruoyi-system/target/classes/mapper/system/SysUserMapper.xml new file mode 100644 index 00000000..1c170fd5 --- /dev/null +++ b/ruoyi-system/target/classes/mapper/system/SysUserMapper.xml @@ -0,0 +1,232 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark,u.tenant_id, + d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.status as dept_status, + r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status as role_status + from sys_user u + left join sys_dept d on u.dept_id = d.dept_id + left join sys_user_role urs on u.user_id = urs.user_id + left join sys_role r on r.role_id = urs.role_id + + + + + + + + + + + + + + + + + + + + insert into sys_user( + user_id, + dept_id, + user_name, + nick_name, + email, + avatar, + phonenumber, + sex, + password, + status, + create_by, + remark, + create_time + )values( + #{userId}, + #{deptId}, + #{userName}, + #{nickName}, + #{email}, + #{avatar}, + #{phonenumber}, + #{sex}, + #{password}, + #{status}, + #{createBy}, + #{remark}, + sysdate() + ) + + + + update sys_user + + dept_id = #{deptId}, + user_name = #{userName}, + nick_name = #{nickName}, + email = #{email}, + phonenumber = #{phonenumber}, + sex = #{sex}, + avatar = #{avatar}, + password = #{password}, + status = #{status}, + login_ip = #{loginIp}, + login_date = #{loginDate}, + update_by = #{updateBy}, + remark = #{remark}, + update_time = sysdate() + + where user_id = #{userId} + + + + update sys_user set status = #{status} where user_id = #{userId} + + + + update sys_user set avatar = #{avatar} where user_name = #{userName} + + + + update sys_user set password = #{password} where user_name = #{userName} + + + + update sys_user set del_flag = '2' where user_id = #{userId} + + + + update sys_user set del_flag = '2' where user_id in + + #{userId} + + + + diff --git a/ruoyi-system/target/classes/mapper/system/SysUserPostMapper.xml b/ruoyi-system/target/classes/mapper/system/SysUserPostMapper.xml new file mode 100644 index 00000000..912f9dfb --- /dev/null +++ b/ruoyi-system/target/classes/mapper/system/SysUserPostMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + delete from sys_user_post where user_id=#{userId} + + + + + + delete from sys_user_post where user_id in + + #{userId} + + + + + insert into sys_user_post(user_id, post_id) values + + (#{item.userId},#{item.postId}) + + + + diff --git a/ruoyi-system/target/classes/mapper/system/SysUserRoleMapper.xml b/ruoyi-system/target/classes/mapper/system/SysUserRoleMapper.xml new file mode 100644 index 00000000..dd726891 --- /dev/null +++ b/ruoyi-system/target/classes/mapper/system/SysUserRoleMapper.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + delete from sys_user_role where user_id=#{userId} + + + + + + delete from sys_user_role where user_id in + + #{userId} + + + + + insert into sys_user_role(user_id, role_id) values + + (#{item.userId},#{item.roleId}) + + + + + delete from sys_user_role where user_id=#{userId} and role_id=#{roleId} + + + + delete from sys_user_role where role_id=#{roleId} and user_id in + + #{userId} + + + \ No newline at end of file diff --git a/ruoyi-system/target/generated-sources/annotations/com/ruoyi/system/convert/Device/DeviceConvertImpl.java b/ruoyi-system/target/generated-sources/annotations/com/ruoyi/system/convert/Device/DeviceConvertImpl.java new file mode 100644 index 00000000..307d3874 --- /dev/null +++ b/ruoyi-system/target/generated-sources/annotations/com/ruoyi/system/convert/Device/DeviceConvertImpl.java @@ -0,0 +1,11 @@ +package com.ruoyi.system.convert.Device; + +import javax.annotation.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2023-07-11T22:37:10+0800", + comments = "version: 1.4.1.Final, compiler: javac, environment: Java 1.8.0_91 (Oracle Corporation)" +) +public class DeviceConvertImpl implements DeviceConvert { +} diff --git a/ruoyi-system/target/generated-sources/annotations/com/ruoyi/system/convert/task/TaskConvertImpl.java b/ruoyi-system/target/generated-sources/annotations/com/ruoyi/system/convert/task/TaskConvertImpl.java new file mode 100644 index 00000000..90f75eb5 --- /dev/null +++ b/ruoyi-system/target/generated-sources/annotations/com/ruoyi/system/convert/task/TaskConvertImpl.java @@ -0,0 +1,11 @@ +package com.ruoyi.system.convert.task; + +import javax.annotation.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2023-07-11T22:37:10+0800", + comments = "version: 1.4.1.Final, compiler: javac, environment: Java 1.8.0_91 (Oracle Corporation)" +) +public class TaskConvertImpl implements TaskConvert { +} diff --git a/ruoyi-system/target/generated-sources/annotations/com/ruoyi/system/convert/tenant/TenantConvertImpl.java b/ruoyi-system/target/generated-sources/annotations/com/ruoyi/system/convert/tenant/TenantConvertImpl.java new file mode 100644 index 00000000..dda3c7fe --- /dev/null +++ b/ruoyi-system/target/generated-sources/annotations/com/ruoyi/system/convert/tenant/TenantConvertImpl.java @@ -0,0 +1,142 @@ +package com.ruoyi.system.convert.tenant; + +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.system.domain.TenantDO; +import com.ruoyi.system.domain.TenantDO.TenantDOBuilder; +import com.ruoyi.system.domain.vo.tenant.TenantCreateReqVO; +import com.ruoyi.system.domain.vo.tenant.TenantExcelVO; +import com.ruoyi.system.domain.vo.tenant.TenantRespVO; +import com.ruoyi.system.domain.vo.tenant.TenantUpdateReqVO; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2023-07-11T22:37:10+0800", + comments = "version: 1.4.1.Final, compiler: javac, environment: Java 1.8.0_91 (Oracle Corporation)" +) +public class TenantConvertImpl implements TenantConvert { + + @Override + public TenantDO convert(TenantCreateReqVO bean) { + if ( bean == null ) { + return null; + } + + TenantDOBuilder tenantDO = TenantDO.builder(); + + tenantDO.name( bean.getName() ); + tenantDO.contactName( bean.getContactName() ); + tenantDO.contactMobile( bean.getContactMobile() ); + tenantDO.status( bean.getStatus() ); + tenantDO.domain( bean.getDomain() ); + tenantDO.packageId( bean.getPackageId() ); + tenantDO.expireTime( bean.getExpireTime() ); + tenantDO.accountCount( bean.getAccountCount() ); + + return tenantDO.build(); + } + + @Override + public TenantDO convert(TenantUpdateReqVO bean) { + if ( bean == null ) { + return null; + } + + TenantDOBuilder tenantDO = TenantDO.builder(); + + tenantDO.id( bean.getId() ); + tenantDO.name( bean.getName() ); + tenantDO.contactName( bean.getContactName() ); + tenantDO.contactMobile( bean.getContactMobile() ); + tenantDO.status( bean.getStatus() ); + tenantDO.domain( bean.getDomain() ); + tenantDO.packageId( bean.getPackageId() ); + tenantDO.expireTime( bean.getExpireTime() ); + tenantDO.accountCount( bean.getAccountCount() ); + + return tenantDO.build(); + } + + @Override + public TenantRespVO convert(TenantDO bean) { + if ( bean == null ) { + return null; + } + + TenantRespVO tenantRespVO = new TenantRespVO(); + + tenantRespVO.setName( bean.getName() ); + tenantRespVO.setContactName( bean.getContactName() ); + tenantRespVO.setContactMobile( bean.getContactMobile() ); + tenantRespVO.setStatus( bean.getStatus() ); + tenantRespVO.setDomain( bean.getDomain() ); + tenantRespVO.setPackageId( bean.getPackageId() ); + tenantRespVO.setExpireTime( bean.getExpireTime() ); + tenantRespVO.setAccountCount( bean.getAccountCount() ); + tenantRespVO.setId( bean.getId() ); + tenantRespVO.setCreateTime( bean.getCreateTime() ); + + return tenantRespVO; + } + + @Override + public List convertList(List list) { + if ( list == null ) { + return null; + } + + List list1 = new ArrayList( list.size() ); + for ( TenantDO tenantDO : list ) { + list1.add( convert( tenantDO ) ); + } + + return list1; + } + + @Override + public PageResult convertPage(PageResult page) { + if ( page == null ) { + return null; + } + + PageResult pageResult = new PageResult(); + + pageResult.setList( convertList( page.getList() ) ); + pageResult.setTotal( page.getTotal() ); + + return pageResult; + } + + @Override + public List convertList02(List list) { + if ( list == null ) { + return null; + } + + List list1 = new ArrayList( list.size() ); + for ( TenantDO tenantDO : list ) { + list1.add( tenantDOToTenantExcelVO( tenantDO ) ); + } + + return list1; + } + + protected TenantExcelVO tenantDOToTenantExcelVO(TenantDO tenantDO) { + if ( tenantDO == null ) { + return null; + } + + TenantExcelVO tenantExcelVO = new TenantExcelVO(); + + tenantExcelVO.setId( tenantDO.getId() ); + tenantExcelVO.setName( tenantDO.getName() ); + tenantExcelVO.setContactName( tenantDO.getContactName() ); + tenantExcelVO.setContactMobile( tenantDO.getContactMobile() ); + tenantExcelVO.setStatus( tenantDO.getStatus() ); + tenantExcelVO.setCreateTime( tenantDO.getCreateTime() ); + + return tenantExcelVO; + } +} diff --git a/ruoyi-system/target/generated-sources/annotations/com/ruoyi/system/convert/tenant/TenantPackageConvertImpl.java b/ruoyi-system/target/generated-sources/annotations/com/ruoyi/system/convert/tenant/TenantPackageConvertImpl.java new file mode 100644 index 00000000..a945299d --- /dev/null +++ b/ruoyi-system/target/generated-sources/annotations/com/ruoyi/system/convert/tenant/TenantPackageConvertImpl.java @@ -0,0 +1,137 @@ +package com.ruoyi.system.convert.tenant; + +import com.ruoyi.common.mybatis.pojo.PageResult; +import com.ruoyi.system.domain.TenantPackageDO; +import com.ruoyi.system.domain.TenantPackageDO.TenantPackageDOBuilder; +import com.ruoyi.system.domain.vo.packages.TenantPackageCreateReqVO; +import com.ruoyi.system.domain.vo.packages.TenantPackageRespVO; +import com.ruoyi.system.domain.vo.packages.TenantPackageSimpleRespVO; +import com.ruoyi.system.domain.vo.packages.TenantPackageUpdateReqVO; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import javax.annotation.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2023-07-11T22:37:10+0800", + comments = "version: 1.4.1.Final, compiler: javac, environment: Java 1.8.0_91 (Oracle Corporation)" +) +public class TenantPackageConvertImpl implements TenantPackageConvert { + + @Override + public TenantPackageDO convert(TenantPackageCreateReqVO bean) { + if ( bean == null ) { + return null; + } + + TenantPackageDOBuilder tenantPackageDO = TenantPackageDO.builder(); + + tenantPackageDO.name( bean.getName() ); + tenantPackageDO.status( bean.getStatus() ); + tenantPackageDO.remark( bean.getRemark() ); + Set set = bean.getMenuIds(); + if ( set != null ) { + tenantPackageDO.menuIds( new HashSet( set ) ); + } + + return tenantPackageDO.build(); + } + + @Override + public TenantPackageDO convert(TenantPackageUpdateReqVO bean) { + if ( bean == null ) { + return null; + } + + TenantPackageDOBuilder tenantPackageDO = TenantPackageDO.builder(); + + tenantPackageDO.id( bean.getId() ); + tenantPackageDO.name( bean.getName() ); + tenantPackageDO.status( bean.getStatus() ); + tenantPackageDO.remark( bean.getRemark() ); + Set set = bean.getMenuIds(); + if ( set != null ) { + tenantPackageDO.menuIds( new HashSet( set ) ); + } + + return tenantPackageDO.build(); + } + + @Override + public TenantPackageRespVO convert(TenantPackageDO bean) { + if ( bean == null ) { + return null; + } + + TenantPackageRespVO tenantPackageRespVO = new TenantPackageRespVO(); + + tenantPackageRespVO.setName( bean.getName() ); + tenantPackageRespVO.setStatus( bean.getStatus() ); + tenantPackageRespVO.setRemark( bean.getRemark() ); + Set set = bean.getMenuIds(); + if ( set != null ) { + tenantPackageRespVO.setMenuIds( new HashSet( set ) ); + } + tenantPackageRespVO.setId( bean.getId() ); + tenantPackageRespVO.setCreateTime( bean.getCreateTime() ); + + return tenantPackageRespVO; + } + + @Override + public List convertList(List list) { + if ( list == null ) { + return null; + } + + List list1 = new ArrayList( list.size() ); + for ( TenantPackageDO tenantPackageDO : list ) { + list1.add( convert( tenantPackageDO ) ); + } + + return list1; + } + + @Override + public PageResult convertPage(PageResult page) { + if ( page == null ) { + return null; + } + + PageResult pageResult = new PageResult(); + + pageResult.setList( convertList( page.getList() ) ); + pageResult.setTotal( page.getTotal() ); + + return pageResult; + } + + @Override + public List convertList02(List list) { + if ( list == null ) { + return null; + } + + List list1 = new ArrayList( list.size() ); + for ( TenantPackageDO tenantPackageDO : list ) { + list1.add( tenantPackageDOToTenantPackageSimpleRespVO( tenantPackageDO ) ); + } + + return list1; + } + + protected TenantPackageSimpleRespVO tenantPackageDOToTenantPackageSimpleRespVO(TenantPackageDO tenantPackageDO) { + if ( tenantPackageDO == null ) { + return null; + } + + TenantPackageSimpleRespVO tenantPackageSimpleRespVO = new TenantPackageSimpleRespVO(); + + tenantPackageSimpleRespVO.setId( tenantPackageDO.getId() ); + tenantPackageSimpleRespVO.setName( tenantPackageDO.getName() ); + + return tenantPackageSimpleRespVO; + } +} diff --git a/ruoyi-system/target/generated-sources/annotations/com/ruoyi/system/convert/user/UserConvertImpl.java b/ruoyi-system/target/generated-sources/annotations/com/ruoyi/system/convert/user/UserConvertImpl.java new file mode 100644 index 00000000..a44ef099 --- /dev/null +++ b/ruoyi-system/target/generated-sources/annotations/com/ruoyi/system/convert/user/UserConvertImpl.java @@ -0,0 +1,43 @@ +package com.ruoyi.system.convert.user; + +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.system.domain.vo.user.UserSimpleRespVO; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2023-07-11T22:37:10+0800", + comments = "version: 1.4.1.Final, compiler: javac, environment: Java 1.8.0_91 (Oracle Corporation)" +) +public class UserConvertImpl implements UserConvert { + + @Override + public List convertList04(List list) { + if ( list == null ) { + return null; + } + + List list1 = new ArrayList( list.size() ); + for ( SysUser sysUser : list ) { + list1.add( sysUserToUserSimpleRespVO( sysUser ) ); + } + + return list1; + } + + protected UserSimpleRespVO sysUserToUserSimpleRespVO(SysUser sysUser) { + if ( sysUser == null ) { + return null; + } + + UserSimpleRespVO userSimpleRespVO = new UserSimpleRespVO(); + + userSimpleRespVO.setUserId( sysUser.getUserId() ); + userSimpleRespVO.setNickName( sysUser.getNickName() ); + userSimpleRespVO.setUserName( sysUser.getUserName() ); + + return userSimpleRespVO; + } +} diff --git a/注意事项.txt b/注意事项.txt new file mode 100644 index 00000000..b9289584 --- /dev/null +++ b/注意事项.txt @@ -0,0 +1,32 @@ +关于代码生成器代码生成后需修改的点: + 1、表实体ID自增的要加注解:@TableId(type = IdType.AUTO) +关于工作流: + 工作流flw_和act_开头的表可以不用建 +本地开发添加Jvm启动参数:-Dspring.profiles.active=dev + +多租户表: + 1、系统表如果是多租户的或者bs_,bpm_开头的非多租户表都要在TenantDatabaseInterceptor里面进行配置 + +部署注意事项: + 1、如果docker部署,需挂载日志与附件的目录 + +数据库存json 对象: + 1、实体字段加注解 @TableField(typeHandler = JacksonTypeHandler.class) + 2、实体加@TableName(value = "bs_xxx", autoResultMap = true),目前代码生成会自动加上这个 + 2、mapperxml 加 typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler" + +权限注意事项: + 1、部门等权限控制使用@DataScope注解,然后mapperxml里面要去关联部门表。勿用mybatis plus + + +开发规范: +图片统一格式: [{"name": "xx","url": "xx"}] + +表设计固定需要字段: +`files` varchar(255) NOT NULL DEFAULT '[]' COMMENT '附件', +`create_by` varchar(64) DEFAULT '' COMMENT '创建者', +`create_time` datetime DEFAULT NULL COMMENT '创建时间', +`update_by` varchar(64) DEFAULT '' COMMENT '更新者', +`update_time` datetime DEFAULT NULL COMMENT '更新时间', +`tenant_id` bigint(20) NOT NULL COMMENT '租户编号', +tenant_id的话看是不是需要分为多租户表 \ No newline at end of file