diff --git a/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md b/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md index 9429a8e86..f5ef7c154 100644 --- a/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md +++ b/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md @@ -1,4 +1,4 @@ -### 更改目的 解决了什么问题 +### 更改目的 解决了什么问题(请提交到dev分支) ### 描述 做了哪些改动 diff --git a/README.md b/README.md index 203a03dbc..b7f5d60b0 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/blob/master/LICENSE) [![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
-[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-4.1.0-success.svg)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus) +[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-4.2.0_beta1-success.svg)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus) [![Spring Boot](https://img.shields.io/badge/Spring%20Boot-2.6-blue.svg)]() [![JDK-8+](https://img.shields.io/badge/JDK-8-green.svg)]() [![JDK-11](https://img.shields.io/badge/JDK-11-green.svg)]() @@ -42,6 +42,7 @@ | 分布式任务调度 | Xxl-Job | [Xxl-Job官网](https://www.xuxueli.com/xxl-job/) | 高性能 高可靠 易扩展 | | 文件存储 | Minio | [Minio文档](https://docs.min.io/) | 本地存储 | | 文件存储 | 七牛、阿里、腾讯 | [OSS使用文档](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages?sort_id=4359146&doc_id=1469725) | 云存储 | +| 短信模块 | 阿里、腾讯 | [短信使用文档](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages?sort_id=5578491&doc_id=1469725) | 短信发送 | | 监控框架 | SpringBoot-Admin | [SpringBoot-Admin文档](https://codecentric.github.io/spring-boot-admin/current/) | 全方位服务监控 | | 校验框架 | Validation | [Validation文档](https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/) | 增强接口安全性、严谨性 支持国际化 | | Excel框架 | Alibaba EasyExcel | [EasyExcel文档](https://www.yuque.com/easyexcel/doc/easyexcel) | 性能优异 扩展性强 | diff --git a/pom.xml b/pom.xml index 4fc098f5b..9f1963baf 100644 --- a/pom.xml +++ b/pom.xml @@ -6,49 +6,46 @@ com.ruoyi ruoyi-vue-plus - 4.1.0 + 4.2.0 RuoYi-Vue-Plus https://gitee.com/JavaLionLi/RuoYi-Vue-Plus RuoYi-Vue-Plus后台管理系统 - 4.1.0 - 2.6.7 + 4.2.0 + 2.6.9 UTF-8 UTF-8 1.8 3.2.2 - 2.2.0 - 1.2.8 + 2.2.2 + 1.2.11 3.0.3 1.5.22 - 4.1.2 - 1.21 - 3.0.5 + 5.2.2 + 3.1.1 2.3 - 1.29.0 - 3.5.1 + 1.30.0 + 3.5.2 3.9.1 - 5.7.22 + 5.8.3 4.9.3 - 2.6.6 - 3.17.0 + 2.6.7 + 3.17.4 2.2.1 3.5.1 - 1.3.6 - 2.3.0 + 1.4.3 + 2.3.1 - - 3.0.1 30.0-jre - 7.9.5 - 3.14.0 - 5.6.72 - 8.3.8 + 1.12.248 + + 2.0.9 + 3.1.537 localhost @@ -105,29 +102,21 @@ ${swagger-annotations.version} - + + org.apache.poi + poi + ${poi.version} + org.apache.poi poi-ooxml ${poi.version} - - - - org.apache.commons - commons-compress - ${commons-compress.version} - - com.alibaba easyexcel ${easyexcel.version} - - org.apache.poi - poi - org.apache.poi poi-ooxml-schemas @@ -155,13 +144,6 @@ ${satoken.version} - - - com.sun.xml.bind - jaxb-impl - ${jaxb.version} - - com.baomidou @@ -192,6 +174,24 @@ ${okhttp.version} + + com.amazonaws + aws-java-sdk-s3 + ${aws-java-sdk-s3.version} + + + + com.aliyun + dysmsapi20170525 + ${aliyun.sms.version} + + + + com.tencentcloudapi + tencentcloud-sdk-java + ${tencent.sms.version} + + de.codecentric spring-boot-admin-starter-server @@ -297,6 +297,13 @@ ${ruoyi-vue-plus.version} + + + com.ruoyi + ruoyi-sms + ${ruoyi-vue-plus.version} + + com.ruoyi @@ -317,6 +324,7 @@ ruoyi-demo ruoyi-extend ruoyi-oss + ruoyi-sms pom diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml index 411d3b9aa..a3d2da6f1 100644 --- a/ruoyi-admin/pom.xml +++ b/ruoyi-admin/pom.xml @@ -5,7 +5,7 @@ ruoyi-vue-plus com.ruoyi - 4.1.0 + 4.2.0 4.0.0 jar 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 index a19be65ea..0889aabc7 100644 --- 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 @@ -4,6 +4,8 @@ import cn.hutool.captcha.AbstractCaptcha; import cn.hutool.captcha.generator.CodeGenerator; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.RandomUtil; +import com.ruoyi.common.annotation.Anonymous; import com.ruoyi.common.constant.Constants; import com.ruoyi.common.core.domain.R; import com.ruoyi.common.enums.CaptchaType; @@ -12,30 +14,68 @@ import com.ruoyi.common.utils.redis.RedisUtils; import com.ruoyi.common.utils.reflect.ReflectUtils; import com.ruoyi.common.utils.spring.SpringUtils; import com.ruoyi.framework.config.properties.CaptchaProperties; +import com.ruoyi.sms.config.properties.SmsProperties; +import com.ruoyi.sms.core.SmsTemplate; +import com.ruoyi.sms.entity.SmsResult; import com.ruoyi.system.service.ISysConfigService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; +import javax.validation.constraints.NotBlank; +import java.time.Duration; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.TimeUnit; /** * 验证码操作处理 * * @author Lion Li */ +@Anonymous +@Slf4j +@Validated @Api(value = "验证码操作处理", tags = {"验证码管理"}) @RequiredArgsConstructor @RestController public class CaptchaController { private final CaptchaProperties captchaProperties; + private final SmsProperties smsProperties; private final ISysConfigService configService; + /** + * 短信验证码 + */ + @ApiOperation("短信验证码") + @GetMapping("/captchaSms") + public R smsCaptcha(@ApiParam("用户手机号") + @NotBlank(message = "{user.phonenumber.not.blank}") + String phonenumber) { + if (smsProperties.getEnabled()) { + R.fail("当前系统没有开启短信功能!"); + } + String key = Constants.CAPTCHA_CODE_KEY + phonenumber; + String code = RandomUtil.randomNumbers(4); + RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION)); + // 验证码模板id 自行处理 (查数据库或写死均可) + String templateId = ""; + Map map = new HashMap<>(1); + map.put("code", code); + SmsTemplate smsTemplate = SpringUtils.getBean(SmsTemplate.class); + SmsResult result = smsTemplate.send(phonenumber, templateId, map); + if (!result.isSuccess()) { + log.error("验证码短信发送异常 => {}", result); + return R.fail(result.getMessage()); + } + return R.ok(); + } + /** * 生成验证码 */ @@ -60,7 +100,7 @@ public class CaptchaController { captcha.setGenerator(codeGenerator); captcha.createCode(); String code = isMath ? getCodeResult(captcha.getCode()) : captcha.getCode(); - RedisUtils.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES); + RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION)); ajax.put("uuid", uuid); ajax.put("img", captcha.getImageBase64()); return R.ok(ajax); 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 index facf6ce99..cdb1d6cd6 100644 --- 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 @@ -2,6 +2,7 @@ package com.ruoyi.web.controller.system; import cn.dev33.satoken.exception.NotLoginException; import cn.dev33.satoken.stp.StpUtil; +import com.ruoyi.common.annotation.Anonymous; import com.ruoyi.common.constant.Constants; import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.domain.entity.SysMenu; @@ -51,6 +52,7 @@ public class SysLoginController { * @param loginBody 登录信息 * @return 结果 */ + @Anonymous @ApiOperation("登录方法") @PostMapping("/login") public R> login(@Validated @RequestBody LoginBody loginBody) { @@ -68,6 +70,7 @@ public class SysLoginController { * @param smsLoginBody 登录信息 * @return 结果 */ + @Anonymous @ApiOperation("短信登录(示例)") @PostMapping("/smsLogin") public R> smsLogin(@Validated @RequestBody SmsLoginBody smsLoginBody) { @@ -84,6 +87,7 @@ public class SysLoginController { * @param xcxCode 小程序code * @return 结果 */ + @Anonymous @ApiOperation("小程序登录(示例)") @PostMapping("/xcxLogin") public R> xcxLogin(@NotBlank(message = "{xcx.code.not.blank}") String xcxCode) { @@ -94,12 +98,14 @@ public class SysLoginController { return R.ok(ajax); } + @Anonymous @ApiOperation("登出方法") @PostMapping("/logout") public R logout() { try { + String username = LoginHelper.getUsername(); StpUtil.logout(); - loginService.logout(LoginHelper.getUsername()); + loginService.logout(username); } catch (NotLoginException e) { } return R.ok("退出成功"); diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssController.java index a128a15ff..d4e52acc6 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssController.java @@ -32,6 +32,7 @@ import java.io.File; import java.io.IOException; import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -58,6 +59,19 @@ public class SysOssController extends BaseController { return iSysOssService.queryPageList(bo, pageQuery); } + /** + * 查询OSS对象基于id串 + */ + @ApiOperation("查询OSS对象基于ID") + @SaCheckPermission("system:oss:list") + @GetMapping("/listByIds/{ossIds}") + public R> listByIds(@ApiParam("OSS对象ID串") + @NotEmpty(message = "主键不能为空") + @PathVariable Long[] ossIds) { + List list = iSysOssService.listByIds(Arrays.asList(ossIds)); + return R.ok(list); + } + /** * 上传OSS对象存储 */ 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 index 84d4c0b3d..22b506f36 100644 --- 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 @@ -1,6 +1,7 @@ package com.ruoyi.web.controller.system; import cn.dev33.satoken.secure.BCrypt; +import cn.hutool.core.io.FileUtil; import com.ruoyi.common.annotation.Log; import com.ruoyi.common.constant.UserConstants; import com.ruoyi.common.core.controller.BaseController; @@ -9,6 +10,7 @@ import com.ruoyi.common.core.domain.entity.SysUser; import com.ruoyi.common.enums.BusinessType; import com.ruoyi.common.helper.LoginHelper; import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.file.MimeTypeUtils; import com.ruoyi.system.domain.SysOss; import com.ruoyi.system.service.ISysOssService; import com.ruoyi.system.service.ISysUserService; @@ -22,6 +24,7 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.io.File; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -117,6 +120,10 @@ public class SysProfileController extends BaseController { public R> avatar(@RequestPart("avatarfile") MultipartFile file) { Map ajax = new HashMap<>(); if (!file.isEmpty()) { + String extension = FileUtil.extName(file.getOriginalFilename()); + if (!StringUtils.equalsAnyIgnoreCase(extension, MimeTypeUtils.IMAGE_EXTENSION)) { + return R.fail("文件格式不正确,请上传" + Arrays.toString(MimeTypeUtils.IMAGE_EXTENSION) + "格式"); + } SysOss oss = iSysOssService.upload(file); String avatar = oss.getUrl(); if (userService.updateUserAvatar(getUsername(), avatar)) { 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 index 6595f06df..b8cedd67a 100644 --- 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 @@ -1,5 +1,6 @@ package com.ruoyi.web.controller.system; +import com.ruoyi.common.annotation.Anonymous; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.domain.model.RegisterBody; @@ -27,6 +28,7 @@ public class SysRegisterController extends BaseController { private final SysRegisterService registerService; private final ISysConfigService configService; + @Anonymous @ApiOperation("用户注册") @PostMapping("/register") public R register(@Validated @RequestBody RegisterBody user) { 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 index 5d9a20615..9b7cb268b 100644 --- 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 @@ -148,7 +148,7 @@ public class SysRoleController extends BaseController { @SaCheckPermission("system:role:remove") @Log(title = "角色管理", businessType = BusinessType.DELETE) @DeleteMapping("/{roleIds}") - public R remove(@ApiParam("岗位ID串") @PathVariable Long[] roleIds) { + public R remove(@ApiParam("角色ID串") @PathVariable Long[] roleIds) { return toAjax(roleService.deleteRoleByIds(roleIds)); } diff --git a/ruoyi-admin/src/main/resources/application-dev.yml b/ruoyi-admin/src/main/resources/application-dev.yml index 45eb5be97..f9327f779 100644 --- a/ruoyi-admin/src/main/resources/application-dev.yml +++ b/ruoyi-admin/src/main/resources/application-dev.yml @@ -130,8 +130,8 @@ spring: port: 6379 # 数据库索引 database: 0 - # 密码 - password: + # 密码(如没有密码请注释掉) + # password: # 连接超时时间 timeout: 10s # 是否开启ssl @@ -156,3 +156,37 @@ redisson: timeout: 3000 # 发布和订阅连接池大小 subscriptionConnectionPoolSize: 50 + +--- # mail 邮件发送 +mail: + enabled: false + host: smtp.163.com + port: 465 + # 是否需要用户名密码验证 + auth: true + # 发送方,遵循RFC-822标准 + from: xxx@163.com + # 用户名(注意:如果使用foxmail邮箱,此处user为qq号) + user: xxx@163.com + # 密码(注意,某些邮箱需要为SMTP服务单独设置密码,详情查看相关帮助) + pass: xxxxxxxxxx + # 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。 + starttlsEnable: true + # 使用SSL安全连接 + sslEnable: true + # SMTP超时时长,单位毫秒,缺省值不超时 + timeout: 0 + # Socket连接超时值,单位毫秒,缺省值不超时 + connectionTimeout: 0 + +--- # sms 短信 +sms: + enabled: false + # 阿里云 dysmsapi.aliyuncs.com + # 腾讯云 sms.tencentcloudapi.com + endpoint: "dysmsapi.aliyuncs.com" + accessKeyId: xxxxxxx + accessKeySecret: xxxxxx + signName: 测试 + # 腾讯专用 + sdkAppId: diff --git a/ruoyi-admin/src/main/resources/application-prod.yml b/ruoyi-admin/src/main/resources/application-prod.yml index 6a28bccfb..def5f1666 100644 --- a/ruoyi-admin/src/main/resources/application-prod.yml +++ b/ruoyi-admin/src/main/resources/application-prod.yml @@ -133,8 +133,8 @@ spring: port: 6379 # 数据库索引 database: 0 - # 密码 - password: + # 密码(如没有密码请注释掉) + # password: # 连接超时时间 timeout: 10s # 是否开启ssl @@ -159,3 +159,37 @@ redisson: timeout: 3000 # 发布和订阅连接池大小 subscriptionConnectionPoolSize: 50 + +--- # mail 邮件发送 +mail: + enabled: false + host: smtp.163.com + port: 465 + # 是否需要用户名密码验证 + auth: true + # 发送方,遵循RFC-822标准 + from: xxx@163.com + # 用户名(注意:如果使用foxmail邮箱,此处user为qq号) + user: xxx@163.com + # 密码(注意,某些邮箱需要为SMTP服务单独设置密码,详情查看相关帮助) + pass: xxxxxxxxxx + # 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。 + starttlsEnable: true + # 使用SSL安全连接 + sslEnable: true + # SMTP超时时长,单位毫秒,缺省值不超时 + timeout: 0 + # Socket连接超时值,单位毫秒,缺省值不超时 + connectionTimeout: 0 + +--- # sms 短信 +sms: + enabled: false + # 阿里云 dysmsapi.aliyuncs.com + # 腾讯云 sms.tencentcloudapi.com + endpoint: "dysmsapi.aliyuncs.com" + accessKeyId: xxxxxxx + accessKeySecret: xxxxxx + signName: 测试 + # 腾讯专用 + sdkAppId: diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index c692fc267..ceaaf9561 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -118,12 +118,6 @@ sa-token: security: # 排除路径 excludes: - - /login - - /smsLogin - - /xcxLogin - - /logout - - /register - - /captchaImage # 静态资源 - /*.html - /**/*.html diff --git a/ruoyi-admin/src/main/resources/logback.xml b/ruoyi-admin/src/main/resources/logback.xml index 4d6b910d3..b78044b35 100644 --- a/ruoyi-admin/src/main/resources/logback.xml +++ b/ruoyi-admin/src/main/resources/logback.xml @@ -97,17 +97,9 @@ - - - - - - - - - + diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml index bb476cbdd..10fec546f 100644 --- a/ruoyi-common/pom.xml +++ b/ruoyi-common/pom.xml @@ -5,7 +5,7 @@ ruoyi-vue-plus com.ruoyi - 4.1.0 + 4.2.0 4.0.0 @@ -58,12 +58,6 @@ jackson-databind - - - org.apache.poi - poi-ooxml - - com.alibaba easyexcel @@ -75,12 +69,6 @@ snakeyaml - - - com.sun.xml.bind - jaxb-impl - - javax.servlet @@ -127,6 +115,11 @@ hutool-extra + + com.sun.mail + jakarta.mail + + org.projectlombok lombok 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 000000000..fe2810083 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Anonymous.java @@ -0,0 +1,18 @@ +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/CellMerge.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/CellMerge.java new file mode 100644 index 000000000..4af822eda --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/CellMerge.java @@ -0,0 +1,24 @@ +package com.ruoyi.common.annotation; + +import com.ruoyi.common.excel.CellMergeStrategy; + +import java.lang.annotation.*; + +/** + * excel 列单元格合并(合并列相同项) + * + * 需搭配 {@link CellMergeStrategy} 策略使用 + * + * @author Lion Li + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface CellMerge { + + /** + * col index + */ + int index() default -1; + +} 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 index 6885e95cf..9aa75f7a4 100644 --- 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 @@ -108,12 +108,6 @@ public class SysUser extends BaseEntity { ) private String password; - @JsonIgnore - @JsonProperty - public String getPassword() { - return password; - } - /** * 帐号状态(0正常 1停用) */ diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/excel/CellMergeStrategy.java b/ruoyi-common/src/main/java/com/ruoyi/common/excel/CellMergeStrategy.java new file mode 100644 index 000000000..04a1bbb8a --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/excel/CellMergeStrategy.java @@ -0,0 +1,114 @@ +package com.ruoyi.common.excel; + +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.write.merge.AbstractMergeStrategy; +import com.ruoyi.common.annotation.CellMerge; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.util.CellRangeAddress; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 列值重复合并策略 + * + * @author Lion Li + */ +@AllArgsConstructor +@Slf4j +public class CellMergeStrategy extends AbstractMergeStrategy { + + private List list; + private boolean hasTitle; + + @Override + protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) { + List cellList = handle(list, hasTitle); + // judge the list is not null + if (CollectionUtils.isNotEmpty(cellList)) { + // the judge is necessary + if (cell.getRowIndex() == 1 && cell.getColumnIndex() == 0) { + for (CellRangeAddress item : cellList) { + sheet.addMergedRegion(item); + } + } + } + } + + @SneakyThrows + private static List handle(List list, boolean hasTitle) { + List cellList = new ArrayList<>(); + if (CollectionUtils.isEmpty(list)) { + return cellList; + } + Class clazz = list.get(0).getClass(); + Field[] fields = clazz.getDeclaredFields(); + // 有注解的字段 + List mergeFields = new ArrayList<>(); + List mergeFieldsIndex = new ArrayList<>(); + for (int i = 0; i < fields.length; i++) { + Field field = fields[i]; + if (field.isAnnotationPresent(CellMerge.class)) { + CellMerge cm = field.getAnnotation(CellMerge.class); + mergeFields.add(field); + mergeFieldsIndex.add(cm.index() == -1 ? i : cm.index()); + } + } + // 行合并开始下标 + int rowIndex = hasTitle ? 1 : 0; + Map map = new HashMap<>(); + // 生成两两合并单元格 + for (int i = 0; i < list.size(); i++) { + for (int j = 0; j < mergeFields.size(); j++) { + Field field = mergeFields.get(j); + String name = field.getName(); + String methodName = "get" + name.substring(0, 1).toUpperCase() + name.substring(1); + Method readMethod = clazz.getMethod(methodName); + Object val = readMethod.invoke(list.get(i)); + + int colNum = mergeFieldsIndex.get(j); + if (!map.containsKey(field)) { + map.put(field, new RepeatCell(val, i)); + } else { + RepeatCell repeatCell = map.get(field); + Object cellValue = repeatCell.getValue(); + if (cellValue == null || "".equals(cellValue)) { + // 空值跳过不合并 + continue; + } + if (cellValue != val) { + if (i - repeatCell.getCurrent() > 1) { + cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum)); + } + map.put(field, new RepeatCell(val, i)); + } else if (i == list.size() - 1) { + if (i > repeatCell.getCurrent()) { + cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum)); + } + } + } + } + } + return cellList; + } + + @Data + @AllArgsConstructor + static class RepeatCell { + + private Object value; + + private int current; + + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/helper/DataPermissionHelper.java b/ruoyi-common/src/main/java/com/ruoyi/common/helper/DataPermissionHelper.java index ec4d56767..0e60485ad 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/helper/DataPermissionHelper.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/helper/DataPermissionHelper.java @@ -1,11 +1,11 @@ package com.ruoyi.common.helper; +import cn.dev33.satoken.context.SaHolder; +import cn.dev33.satoken.context.model.SaStorage; import cn.hutool.core.util.ObjectUtil; -import com.ruoyi.common.utils.ServletUtils; import lombok.AccessLevel; import lombok.NoArgsConstructor; -import javax.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; @@ -33,11 +33,11 @@ public class DataPermissionHelper { } public static Map getContext() { - HttpServletRequest request = ServletUtils.getRequest(); - Object attribute = request.getAttribute(DATA_PERMISSION_KEY); + SaStorage saStorage = SaHolder.getStorage(); + Object attribute = saStorage.get(DATA_PERMISSION_KEY); if (ObjectUtil.isNull(attribute)) { - request.setAttribute(DATA_PERMISSION_KEY, new HashMap<>()); - attribute = request.getAttribute(DATA_PERMISSION_KEY); + saStorage.set(DATA_PERMISSION_KEY, new HashMap<>()); + attribute = saStorage.get(DATA_PERMISSION_KEY); } if (attribute instanceof Map) { return (Map) attribute; diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/helper/LoginHelper.java b/ruoyi-common/src/main/java/com/ruoyi/common/helper/LoginHelper.java index 7875c5630..09e1dc757 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/helper/LoginHelper.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/helper/LoginHelper.java @@ -1,5 +1,6 @@ package com.ruoyi.common.helper; +import cn.dev33.satoken.context.SaHolder; import cn.dev33.satoken.stp.StpUtil; import cn.hutool.core.util.ObjectUtil; import com.ruoyi.common.constant.UserConstants; @@ -13,7 +14,7 @@ import lombok.NoArgsConstructor; /** * 登录鉴权助手 - * + * * user_type 为 用户类型 同一个用户表 可以有多种用户类型 例如 pc,app * deivce 为 设备类型 同一个用户类型 可以有 多种设备类型 例如 web,ios * 可以组成 用户类型与设备类型多对多的 权限灵活控制 @@ -29,15 +30,13 @@ public class LoginHelper { public static final String JOIN_CODE = ":"; public static final String LOGIN_USER_KEY = "loginUser"; - private static final ThreadLocal LOGIN_CACHE = new ThreadLocal<>(); - /** * 登录系统 * * @param loginUser 登录用户信息 */ public static void login(LoginUser loginUser) { - LOGIN_CACHE.set(loginUser); + SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser); StpUtil.login(loginUser.getLoginId()); setLoginUser(loginUser); } @@ -49,7 +48,7 @@ public class LoginHelper { * @param loginUser 登录用户信息 */ public static void loginByDevice(LoginUser loginUser, DeviceType deviceType) { - LOGIN_CACHE.set(loginUser); + SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser); StpUtil.login(loginUser.getLoginId(), deviceType.getDevice()); setLoginUser(loginUser); } @@ -65,18 +64,13 @@ public class LoginHelper { * 获取用户(多级缓存) */ public static LoginUser getLoginUser() { - LoginUser loginUser = LOGIN_CACHE.get(); + LoginUser loginUser = (LoginUser) SaHolder.getStorage().get(LOGIN_USER_KEY); if (loginUser != null) { return loginUser; } - return (LoginUser) StpUtil.getTokenSession().get(LOGIN_USER_KEY); - } - - /** - * 清除一级缓存 防止内存问题 - */ - public static void clearCache() { - LOGIN_CACHE.remove(); + loginUser = (LoginUser) StpUtil.getTokenSession().get(LOGIN_USER_KEY); + SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser); + return loginUser; } /** diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/jackson/SensitiveJsonSerializer.java b/ruoyi-common/src/main/java/com/ruoyi/common/jackson/SensitiveJsonSerializer.java index 55c4e6a7d..404f393fe 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/jackson/SensitiveJsonSerializer.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/jackson/SensitiveJsonSerializer.java @@ -27,9 +27,9 @@ public class SensitiveJsonSerializer extends JsonSerializer implements C public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException { SensitiveService sensitiveService = SpringUtils.getBean(SensitiveService.class); if (sensitiveService.isSensitive()) { - gen.writeString(value); - } else { gen.writeString(strategy.desensitizer().apply(value)); + } else { + gen.writeString(value); } } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/email/MailUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/email/MailUtils.java new file mode 100644 index 000000000..32a3f8252 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/email/MailUtils.java @@ -0,0 +1,468 @@ +package com.ruoyi.common.utils.email; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.CharUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.mail.*; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import javax.mail.Authenticator; +import javax.mail.Session; +import java.io.File; +import java.io.InputStream; +import java.util.Collection; +import java.util.List; +import java.util.Map; + + +/** + * 邮件工具类 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class MailUtils { + + private static final MailAccount ACCOUNT = SpringUtils.getBean(MailAccount.class); + + /** + * 获取邮件发送实例 + */ + public static MailAccount getMailAccount() { + return ACCOUNT; + } + + /** + * 获取邮件发送实例 (自定义发送人以及授权码) + * + * @param user 发送人 + * @param pass 授权码 + */ + public static MailAccount getMailAccount(String from, String user, String pass) { + ACCOUNT.setFrom(StringUtils.blankToDefault(from, ACCOUNT.getFrom())); + ACCOUNT.setUser(StringUtils.blankToDefault(user, ACCOUNT.getUser())); + ACCOUNT.setPass(StringUtils.blankToDefault(pass, ACCOUNT.getPass())); + return ACCOUNT; + } + + /** + * 使用配置文件中设置的账户发送文本邮件,发送给单个或多个收件人
+ * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人 + * @param subject 标题 + * @param content 正文 + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String sendText(String to, String subject, String content, File... files) { + return send(to, subject, content, false, files); + } + + /** + * 使用配置文件中设置的账户发送HTML邮件,发送给单个或多个收件人
+ * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人 + * @param subject 标题 + * @param content 正文 + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String sendHtml(String to, String subject, String content, File... files) { + return send(to, subject, content, true, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
+ * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + */ + public static String send(String to, String subject, String content, boolean isHtml, File... files) { + return send(splitAddress(to), subject, content, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
+ * 多个收件人、抄送人、密送人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * @param cc 抄送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * @param bcc 密送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + * @since 4.0.3 + */ + public static String send(String to, String cc, String bcc, String subject, String content, boolean isHtml, File... files) { + return send(splitAddress(to), splitAddress(cc), splitAddress(bcc), subject, content, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送文本邮件,发送给多人 + * + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param files 附件列表 + * @return message-id + */ + public static String sendText(Collection tos, String subject, String content, File... files) { + return send(tos, subject, content, false, files); + } + + /** + * 使用配置文件中设置的账户发送HTML邮件,发送给多人 + * + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String sendHtml(Collection tos, String subject, String content, File... files) { + return send(tos, subject, content, true, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送给多人 + * + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + */ + public static String send(Collection tos, String subject, String content, boolean isHtml, File... files) { + return send(tos, null, null, subject, content, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送给多人 + * + * @param tos 收件人列表 + * @param ccs 抄送人列表,可以为null或空 + * @param bccs 密送人列表,可以为null或空 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + * @since 4.0.3 + */ + public static String send(Collection tos, Collection ccs, Collection bccs, String subject, String content, boolean isHtml, File... files) { + return send(getMailAccount(), true, tos, ccs, bccs, subject, content, null, isHtml, files); + } + + // ------------------------------------------------------------------------------------------------------------------------------- Custom MailAccount + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件认证对象 + * @param to 收件人,多个收件人逗号或者分号隔开 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String send(MailAccount mailAccount, String to, String subject, String content, boolean isHtml, File... files) { + return send(mailAccount, splitAddress(to), subject, content, isHtml, files); + } + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件帐户信息 + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + */ + public static String send(MailAccount mailAccount, Collection tos, String subject, String content, boolean isHtml, File... files) { + return send(mailAccount, tos, null, null, subject, content, isHtml, files); + } + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件帐户信息 + * @param tos 收件人列表 + * @param ccs 抄送人列表,可以为null或空 + * @param bccs 密送人列表,可以为null或空 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + * @since 4.0.3 + */ + public static String send(MailAccount mailAccount, Collection tos, Collection ccs, Collection bccs, String subject, String content, boolean isHtml, File... files) { + return send(mailAccount, false, tos, ccs, bccs, subject, content, null, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送HTML邮件,发送给单个或多个收件人
+ * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String sendHtml(String to, String subject, String content, Map imageMap, File... files) { + return send(to, subject, content, imageMap, true, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
+ * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + */ + public static String send(String to, String subject, String content, Map imageMap, boolean isHtml, File... files) { + return send(splitAddress(to), subject, content, imageMap, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
+ * 多个收件人、抄送人、密送人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * @param cc 抄送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * @param bcc 密送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + * @since 4.0.3 + */ + public static String send(String to, String cc, String bcc, String subject, String content, Map imageMap, boolean isHtml, File... files) { + return send(splitAddress(to), splitAddress(cc), splitAddress(bcc), subject, content, imageMap, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送HTML邮件,发送给多人 + * + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String sendHtml(Collection tos, String subject, String content, Map imageMap, File... files) { + return send(tos, subject, content, imageMap, true, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送给多人 + * + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + */ + public static String send(Collection tos, String subject, String content, Map imageMap, boolean isHtml, File... files) { + return send(tos, null, null, subject, content, imageMap, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送给多人 + * + * @param tos 收件人列表 + * @param ccs 抄送人列表,可以为null或空 + * @param bccs 密送人列表,可以为null或空 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + * @since 4.0.3 + */ + public static String send(Collection tos, Collection ccs, Collection bccs, String subject, String content, Map imageMap, boolean isHtml, File... files) { + return send(getMailAccount(), true, tos, ccs, bccs, subject, content, imageMap, isHtml, files); + } + + // ------------------------------------------------------------------------------------------------------------------------------- Custom MailAccount + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件认证对象 + * @param to 收件人,多个收件人逗号或者分号隔开 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String send(MailAccount mailAccount, String to, String subject, String content, Map imageMap, boolean isHtml, File... files) { + return send(mailAccount, splitAddress(to), subject, content, imageMap, isHtml, files); + } + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件帐户信息 + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + * @since 4.6.3 + */ + public static String send(MailAccount mailAccount, Collection tos, String subject, String content, Map imageMap, boolean isHtml, File... files) { + return send(mailAccount, tos, null, null, subject, content, imageMap, isHtml, files); + } + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件帐户信息 + * @param tos 收件人列表 + * @param ccs 抄送人列表,可以为null或空 + * @param bccs 密送人列表,可以为null或空 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + * @since 4.6.3 + */ + public static String send(MailAccount mailAccount, Collection tos, Collection ccs, Collection bccs, String subject, String content, Map imageMap, + boolean isHtml, File... files) { + return send(mailAccount, false, tos, ccs, bccs, subject, content, imageMap, isHtml, files); + } + + /** + * 根据配置文件,获取邮件客户端会话 + * + * @param mailAccount 邮件账户配置 + * @param isSingleton 是否单例(全局共享会话) + * @return {@link Session} + * @since 5.5.7 + */ + public static Session getSession(MailAccount mailAccount, boolean isSingleton) { + Authenticator authenticator = null; + if (mailAccount.isAuth()) { + authenticator = new UserPassAuthenticator(mailAccount.getUser(), mailAccount.getPass()); + } + + return isSingleton ? Session.getDefaultInstance(mailAccount.getSmtpProps(), authenticator) // + : Session.getInstance(mailAccount.getSmtpProps(), authenticator); + } + + // ------------------------------------------------------------------------------------------------------------------------ Private method start + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件帐户信息 + * @param useGlobalSession 是否全局共享Session + * @param tos 收件人列表 + * @param ccs 抄送人列表,可以为null或空 + * @param bccs 密送人列表,可以为null或空 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:${cid} + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + * @since 4.6.3 + */ + private static String send(MailAccount mailAccount, boolean useGlobalSession, Collection tos, Collection ccs, Collection bccs, String subject, String content, + Map imageMap, boolean isHtml, File... files) { + final Mail mail = Mail.create(mailAccount).setUseGlobalSession(useGlobalSession); + + // 可选抄送人 + if (CollUtil.isNotEmpty(ccs)) { + mail.setCcs(ccs.toArray(new String[0])); + } + // 可选密送人 + if (CollUtil.isNotEmpty(bccs)) { + mail.setBccs(bccs.toArray(new String[0])); + } + + mail.setTos(tos.toArray(new String[0])); + mail.setTitle(subject); + mail.setContent(content); + mail.setHtml(isHtml); + mail.setFiles(files); + + // 图片 + if (MapUtil.isNotEmpty(imageMap)) { + for (Map.Entry entry : imageMap.entrySet()) { + mail.addImage(entry.getKey(), entry.getValue()); + // 关闭流 + IoUtil.close(entry.getValue()); + } + } + + return mail.send(); + } + + /** + * 将多个联系人转为列表,分隔符为逗号或者分号 + * + * @param addresses 多个联系人,如果为空返回null + * @return 联系人列表 + */ + private static List splitAddress(String addresses) { + if (StrUtil.isBlank(addresses)) { + return null; + } + + List result; + if (StrUtil.contains(addresses, CharUtil.COMMA)) { + result = StrUtil.splitTrim(addresses, CharUtil.COMMA); + } else if (StrUtil.contains(addresses, ';')) { + result = StrUtil.splitTrim(addresses, ';'); + } else { + result = CollUtil.newArrayList(addresses); + } + return result; + } + // ------------------------------------------------------------------------------------------------------------------------ Private method end + +} 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 000000000..6ca97fe60 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java @@ -0,0 +1,40 @@ +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"}; + +} 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 index 36dc67fa1..e6a67b26b 100644 --- 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 @@ -1,9 +1,17 @@ package com.ruoyi.common.utils.poi; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.io.resource.ClassPathResource; import cn.hutool.core.util.IdUtil; import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder; +import com.alibaba.excel.write.metadata.WriteSheet; +import com.alibaba.excel.write.metadata.fill.FillConfig; +import com.alibaba.excel.write.metadata.fill.FillWrapper; import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; import com.ruoyi.common.convert.ExcelBigNumberConvert; +import com.ruoyi.common.excel.CellMergeStrategy; import com.ruoyi.common.excel.DefaultExcelListener; import com.ruoyi.common.excel.ExcelListener; import com.ruoyi.common.excel.ExcelResult; @@ -16,7 +24,10 @@ import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.util.Collection; import java.util.List; +import java.util.Map; /** * Excel相关处理 @@ -69,27 +80,125 @@ public class ExcelUtil { * * @param list 导出数据集合 * @param sheetName 工作表的名称 - * @return 结果 + * @param clazz 实体类 + * @param response 响应体 */ public static void exportExcel(List list, String sheetName, Class clazz, HttpServletResponse response) { + exportExcel(list, sheetName, clazz, false, response); + } + + /** + * 导出excel + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param clazz 实体类 + * @param merge 是否合并单元格 + * @param response 响应体 + */ + public static void exportExcel(List list, String sheetName, Class clazz, boolean merge, HttpServletResponse response) { try { - String filename = encodingFilename(sheetName); - response.reset(); - FileUtils.setAttachmentResponseHeader(response, filename); - response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8"); + resetResponse(sheetName, response); ServletOutputStream os = response.getOutputStream(); - EasyExcel.write(os, clazz) + ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz) .autoCloseStream(false) // 自动适配 .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 大数值自动转换 防止失真 .registerConverter(new ExcelBigNumberConvert()) - .sheet(sheetName).doWrite(list); + .sheet(sheetName); + if (merge) { + // 合并处理器 + builder.registerWriteHandler(new CellMergeStrategy(list, true)); + } + builder.doWrite(list); } catch (IOException e) { throw new RuntimeException("导出Excel异常"); } } + /** + * 单表多数据模板导出 模板格式为 {.属性} + * + * @param filename 文件名 + * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 + * 例如: excel/temp.xlsx + * 重点: 模板文件必须放置到启动类对应的 resource 目录下 + * @param data 模板需要的数据 + */ + public static void exportTemplate(List data, String filename, String templatePath, HttpServletResponse response) { + try { + resetResponse(filename, response); + ClassPathResource templateResource = new ClassPathResource(templatePath); + ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()) + .withTemplate(templateResource.getStream()) + .autoCloseStream(false) + // 大数值自动转换 防止失真 + .registerConverter(new ExcelBigNumberConvert()) + .build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + if (CollUtil.isEmpty(data)) { + throw new IllegalArgumentException("数据为空"); + } + // 单表多数据导出 模板格式为 {.属性} + for (Object d : data) { + excelWriter.fill(d, writeSheet); + } + excelWriter.finish(); + } catch (IOException e) { + throw new RuntimeException("导出Excel异常"); + } + } + + /** + * 多表多数据模板导出 模板格式为 {key.属性} + * + * @param filename 文件名 + * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 + * 例如: excel/temp.xlsx + * 重点: 模板文件必须放置到启动类对应的 resource 目录下 + * @param data 模板需要的数据 + */ + public static void exportTemplateMultiList(Map data, String filename, String templatePath, HttpServletResponse response) { + try { + resetResponse(filename, response); + ClassPathResource templateResource = new ClassPathResource(templatePath); + ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()) + .withTemplate(templateResource.getStream()) + .autoCloseStream(false) + // 大数值自动转换 防止失真 + .registerConverter(new ExcelBigNumberConvert()) + .build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + if (CollUtil.isEmpty(data)) { + throw new IllegalArgumentException("数据为空"); + } + for (Map.Entry map : data.entrySet()) { + // 设置列表后续还有数据 + FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); + if (map.getValue() instanceof Collection) { + // 多表导出必须使用 FillWrapper + excelWriter.fill(new FillWrapper(map.getKey(), (Collection) map.getValue()), fillConfig, writeSheet); + } else { + excelWriter.fill(map.getValue(), writeSheet); + } + } + excelWriter.finish(); + } catch (IOException e) { + throw new RuntimeException("导出Excel异常"); + } + } + + /** + * 重置响应体 + */ + private static void resetResponse(String sheetName, HttpServletResponse response) throws UnsupportedEncodingException { + String filename = encodingFilename(sheetName); + response.reset(); + FileUtils.setAttachmentResponseHeader(response, filename); + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8"); + } + /** * 解析导出值 0=男,1=女,2=未知 * @@ -103,7 +212,7 @@ public class ExcelUtil { String[] convertSource = converterExp.split(","); for (String item : convertSource) { String[] itemArray = item.split("="); - if (StringUtils.containsAny(separator, propertyValue)) { + if (StringUtils.containsAny(propertyValue, separator)) { for (String value : propertyValue.split(separator)) { if (itemArray[0].equals(value)) { propertyString.append(itemArray[1] + separator); @@ -132,7 +241,7 @@ public class ExcelUtil { String[] convertSource = converterExp.split(","); for (String item : convertSource) { String[] itemArray = item.split("="); - if (StringUtils.containsAny(separator, propertyValue)) { + if (StringUtils.containsAny(propertyValue, separator)) { for (String value : propertyValue.split(separator)) { if (itemArray[1].equals(value)) { propertyString.append(itemArray[0] + separator); diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/redis/RedisUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/redis/RedisUtils.java index 5a15d46b7..7ed3b2827 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/redis/RedisUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/redis/RedisUtils.java @@ -6,11 +6,11 @@ import lombok.AccessLevel; import lombok.NoArgsConstructor; import org.redisson.api.*; +import java.time.Duration; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.TimeUnit; import java.util.function.Consumer; /** @@ -107,7 +107,7 @@ public class RedisUtils { } catch (Exception e) { long timeToLive = bucket.remainTimeToLive(); bucket.set(value); - bucket.expire(timeToLive, TimeUnit.MILLISECONDS); + bucket.expire(Duration.ofMillis(timeToLive)); } } else { bucket.set(value); @@ -119,13 +119,12 @@ public class RedisUtils { * * @param key 缓存的键值 * @param value 缓存的值 - * @param timeout 时间 - * @param timeUnit 时间颗粒度 + * @param duration 时间 */ - public static void setCacheObject(final String key, final T value, final long timeout, final TimeUnit timeUnit) { + public static void setCacheObject(final String key, final T value, final Duration duration) { RBucket result = CLIENT.getBucket(key); result.set(value); - result.expire(timeout, timeUnit); + result.expire(duration); } /** @@ -149,20 +148,19 @@ public class RedisUtils { * @return true=设置成功;false=设置失败 */ public static boolean expire(final String key, final long timeout) { - return expire(key, timeout, TimeUnit.SECONDS); + return expire(key, Duration.ofSeconds(timeout)); } /** * 设置有效时间 * - * @param key Redis键 - * @param timeout 超时时间 - * @param unit 时间单位 + * @param key Redis键 + * @param duration 超时时间 * @return true=设置成功;false=设置失败 */ - public static boolean expire(final String key, final long timeout, final TimeUnit unit) { + public static boolean expire(final String key, final Duration duration) { RBucket rBucket = CLIENT.getBucket(key); - return rBucket.expire(timeout, unit); + return rBucket.expire(duration); } /** @@ -366,6 +364,50 @@ public class RedisUtils { return rMap.getAll(hKeys); } + /** + * 设置原子值 + * + * @param key Redis键 + * @param value 值 + */ + public static void setAtomicValue(String key, long value) { + RAtomicLong atomic = CLIENT.getAtomicLong(key); + atomic.set(value); + } + + /** + * 获取原子值 + * + * @param key Redis键 + * @return 当前值 + */ + public static long getAtomicValue(String key) { + RAtomicLong atomic = CLIENT.getAtomicLong(key); + return atomic.get(); + } + + /** + * 递增原子值 + * + * @param key Redis键 + * @return 当前值 + */ + public static long incrAtomicValue(String key) { + RAtomicLong atomic = CLIENT.getAtomicLong(key); + return atomic.incrementAndGet(); + } + + /** + * 递减原子值 + * + * @param key Redis键 + * @return 当前值 + */ + public static long decrAtomicValue(String key) { + RAtomicLong atomic = CLIENT.getAtomicLong(key); + return atomic.decrementAndGet(); + } + /** * 获得缓存的基本对象列表 * diff --git a/ruoyi-demo/pom.xml b/ruoyi-demo/pom.xml index 2fe2c184e..bbcf0fe2b 100644 --- a/ruoyi-demo/pom.xml +++ b/ruoyi-demo/pom.xml @@ -5,7 +5,7 @@ ruoyi-vue-plus com.ruoyi - 4.1.0 + 4.2.0 4.0.0 @@ -23,6 +23,22 @@ ruoyi-common + + com.ruoyi + ruoyi-sms + + + + + + + + + + + + + diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/MailController.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/MailController.java new file mode 100644 index 000000000..6dabe5132 --- /dev/null +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/MailController.java @@ -0,0 +1,48 @@ +package com.ruoyi.demo.controller; + +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.email.MailUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import lombok.RequiredArgsConstructor; +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.RestController; + +import java.io.File; + + +/** + * 邮件发送案例 + * + * @author Michelle.Chung + */ +@Validated +@Api(value = "邮件发送案例", tags = {"邮件发送案例"}) +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/mail") +public class MailController { + + @ApiOperation("发送邮件") + @GetMapping("/sendSimpleMessage") + public R sendSimpleMessage(@ApiParam("接收人") String to, + @ApiParam("标题") String subject, + @ApiParam("内容") String text) { + MailUtils.sendText(to, subject, text); + return R.ok(); + } + + @ApiOperation("发送邮件(带附件)") + @GetMapping("/sendMessageWithAttachment") + public R sendMessageWithAttachment(@ApiParam("接收人") String to, + @ApiParam("标题") String subject, + @ApiParam("内容") String text, + @ApiParam("附件路径") String filePath) { + MailUtils.sendText(to, subject, text, new File(filePath)); + return R.ok(); + } + +} diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisCacheController.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisCacheController.java index 98cca18c0..9fc93074f 100644 --- a/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisCacheController.java +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisCacheController.java @@ -12,7 +12,7 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import java.util.concurrent.TimeUnit; +import java.time.Duration; /** * spring-cache 演示案例 @@ -87,7 +87,7 @@ public class RedisCacheController { @GetMapping("/test6") public R test6(String key, String value) { RedisUtils.setCacheObject(key, value); - boolean flag = RedisUtils.expire(key, 10, TimeUnit.SECONDS); + boolean flag = RedisUtils.expire(key, Duration.ofSeconds(10)); System.out.println("***********" + flag); try { Thread.sleep(11 * 1000); diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/SmsController.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/SmsController.java new file mode 100644 index 000000000..b92078624 --- /dev/null +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/SmsController.java @@ -0,0 +1,72 @@ +package com.ruoyi.demo.controller; + +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.sms.config.properties.SmsProperties; +import com.ruoyi.sms.core.SmsTemplate; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import lombok.RequiredArgsConstructor; +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.RestController; + +import java.util.HashMap; +import java.util.Map; + +/** + * 短信演示案例 + * 请先阅读文档 否则无法使用 + * + * @author Lion Li + * @version 4.2.0 + */ +@Validated +@Api(value = "短信演示案例", tags = {"短信演示案例"}) +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/sms") +public class SmsController { + + private final SmsProperties smsProperties; +// private final SmsTemplate smsTemplate; // 可以使用spring注入 +// private final AliyunSmsTemplate smsTemplate; // 也可以注入某个厂家的模板工具 + + @ApiOperation("发送短信Aliyun") + @GetMapping("/sendAliyun") + public R sendAliyun(@ApiParam("电话号") String phones, + @ApiParam("模板ID") String templateId) { + if (!smsProperties.getEnabled()) { + return R.fail("当前系统没有开启短信功能!"); + } + if (!SpringUtils.containsBean("aliyunSmsTemplate")) { + return R.fail("阿里云依赖未引入!"); + } + SmsTemplate smsTemplate = SpringUtils.getBean(SmsTemplate.class); + Map map = new HashMap<>(1); + map.put("code", "1234"); + Object send = smsTemplate.send(phones, templateId, map); + return R.ok(send); + } + + @ApiOperation("发送短信Tencent") + @GetMapping("/sendTencent") + public R sendTencent(@ApiParam("电话号") String phones, + @ApiParam("模板ID") String templateId) { + if (!smsProperties.getEnabled()) { + return R.fail("当前系统没有开启短信功能!"); + } + if (!SpringUtils.containsBean("tencentSmsTemplate")) { + return R.fail("腾讯云依赖未引入!"); + } + SmsTemplate smsTemplate = SpringUtils.getBean(SmsTemplate.class); + Map map = new HashMap<>(1); +// map.put("2", "测试测试"); + map.put("1", "1234"); + Object send = smsTemplate.send(phones, templateId, map); + return R.ok(send); + } + +} diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java index d1f994e5d..9f00c0770 100644 --- a/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java @@ -29,6 +29,7 @@ import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; +import java.io.File; import java.util.Arrays; import java.util.List; import java.util.concurrent.TimeUnit; @@ -70,7 +71,7 @@ public class TestDemoController extends BaseController { @ApiOperation("导入测试-校验") @ApiImplicitParams({ - @ApiImplicitParam(name = "file", value = "导入文件", dataType = "java.io.File", required = true), + @ApiImplicitParam(name = "file", value = "导入文件", paramType = "query", dataTypeClass = File.class, required = true), }) @Log(title = "测试单表", businessType = BusinessType.IMPORT) @SaCheckPermission("demo:demo:import") diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestExcelController.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestExcelController.java new file mode 100644 index 000000000..a318b4658 --- /dev/null +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestExcelController.java @@ -0,0 +1,102 @@ +package com.ruoyi.demo.controller; + +import cn.hutool.core.collection.CollUtil; +import com.ruoyi.common.utils.poi.ExcelUtil; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.AllArgsConstructor; +import lombok.Data; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletResponse; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 测试Excel功能 + * + * @author Lion Li + */ +@Api(value = "测试Excel功能", tags = {"测试Excel功能"}) +@RestController +@RequestMapping("/demo/excel") +public class TestExcelController { + + /** + * 单列表多数据 + */ + @ApiOperation(value = "单列表多数据") + @GetMapping("/exportTemplateOne") + public void exportTemplateOne(HttpServletResponse response) { + Map map = new HashMap<>(); + map.put("title","单列表多数据"); + map.put("test1","数据测试1"); + map.put("test2","数据测试2"); + map.put("test3","数据测试3"); + map.put("test4","数据测试4"); + map.put("testTest","666"); + List list = new ArrayList<>(); + list.add(new TestObj("单列表测试1", "列表测试1", "列表测试2", "列表测试3", "列表测试4")); + list.add(new TestObj("单列表测试2", "列表测试5", "列表测试6", "列表测试7", "列表测试8")); + list.add(new TestObj("单列表测试3", "列表测试9", "列表测试10", "列表测试11", "列表测试12")); + ExcelUtil.exportTemplate(CollUtil.newArrayList(map,list),"单列表.xlsx", "excel/单列表.xlsx", response); + } + + /** + * 多列表多数据 + */ + @ApiOperation(value = "多列表多数据") + @GetMapping("/exportTemplateMuliti") + public void exportTemplateMuliti(HttpServletResponse response) { + Map map = new HashMap<>(); + map.put("title1","标题1"); + map.put("title2","标题2"); + map.put("title3","标题3"); + map.put("title4","标题4"); + map.put("author","Lion Li"); + List list1 = new ArrayList<>(); + list1.add(new TestObj1("list1测试1", "list1测试2", "list1测试3")); + list1.add(new TestObj1("list1测试4", "list1测试5", "list1测试6")); + list1.add(new TestObj1("list1测试7", "list1测试8", "list1测试9")); + List list2 = new ArrayList<>(); + list2.add(new TestObj1("list2测试1", "list2测试2", "list2测试3")); + list2.add(new TestObj1("list2测试4", "list2测试5", "list2测试6")); + List list3 = new ArrayList<>(); + list3.add(new TestObj1("list3测试1", "list3测试2", "list3测试3")); + List list4 = new ArrayList<>(); + list4.add(new TestObj1("list4测试1", "list4测试2", "list4测试3")); + list4.add(new TestObj1("list4测试4", "list4测试5", "list4测试6")); + list4.add(new TestObj1("list4测试7", "list4测试8", "list4测试9")); + list4.add(new TestObj1("list4测试10", "list4测试11", "list4测试12")); + Map multiListMap = new HashMap<>(); + multiListMap.put("map",map); + multiListMap.put("data1",list1); + multiListMap.put("data2",list2); + multiListMap.put("data3",list3); + multiListMap.put("data4",list4); + ExcelUtil.exportTemplateMultiList(multiListMap, "多列表.xlsx", "excel/多列表.xlsx", response); + } + + @Data + @AllArgsConstructor + static class TestObj1 { + private String test1; + private String test2; + private String test3; + } + + @Data + @AllArgsConstructor + static class TestObj { + private String name; + private String list1; + private String list2; + private String list3; + private String list4; + } + +} diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/bo/TestDemoBo.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/bo/TestDemoBo.java index 5c41204d7..61c10ab16 100644 --- a/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/bo/TestDemoBo.java +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/bo/TestDemoBo.java @@ -49,7 +49,7 @@ public class TestDemoBo extends BaseEntity { */ @ApiModelProperty("排序号") @NotNull(message = "排序号不能为空", groups = {AddGroup.class, EditGroup.class}) - private Long orderNum; + private Integer orderNum; /** * key键 diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/vo/TestDemoVo.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/vo/TestDemoVo.java index ef26ff325..1e896500d 100644 --- a/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/vo/TestDemoVo.java +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/vo/TestDemoVo.java @@ -48,7 +48,7 @@ public class TestDemoVo { */ @ExcelProperty(value = "排序号") @ApiModelProperty("排序号") - private Long orderNum; + private Integer orderNum; /** * key键 diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestDemoMapper.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestDemoMapper.java index b1b80d26e..11a3d50e2 100644 --- a/ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestDemoMapper.java +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestDemoMapper.java @@ -54,5 +54,5 @@ public interface TestDemoMapper extends BaseMapperPlus idList); + int deleteBatchIds(@Param(Constants.COLL) Collection idList); } diff --git a/ruoyi-demo/src/main/resources/excel/单列表.xlsx b/ruoyi-demo/src/main/resources/excel/单列表.xlsx new file mode 100644 index 000000000..0f7347d65 Binary files /dev/null and b/ruoyi-demo/src/main/resources/excel/单列表.xlsx differ diff --git a/ruoyi-demo/src/main/resources/excel/多列表.xlsx b/ruoyi-demo/src/main/resources/excel/多列表.xlsx new file mode 100644 index 000000000..c7d11dcce Binary files /dev/null and b/ruoyi-demo/src/main/resources/excel/多列表.xlsx differ diff --git a/ruoyi-extend/pom.xml b/ruoyi-extend/pom.xml index 5e3725e57..2be8693e4 100644 --- a/ruoyi-extend/pom.xml +++ b/ruoyi-extend/pom.xml @@ -5,7 +5,7 @@ ruoyi-vue-plus com.ruoyi - 4.1.0 + 4.2.0 4.0.0 ruoyi-extend diff --git a/ruoyi-extend/ruoyi-monitor-admin/pom.xml b/ruoyi-extend/ruoyi-monitor-admin/pom.xml index 8fed38aad..e42e8e0f4 100644 --- a/ruoyi-extend/ruoyi-monitor-admin/pom.xml +++ b/ruoyi-extend/ruoyi-monitor-admin/pom.xml @@ -5,7 +5,7 @@ ruoyi-extend com.ruoyi - 4.1.0 + 4.2.0 4.0.0 jar diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/pom.xml b/ruoyi-extend/ruoyi-xxl-job-admin/pom.xml index d61f79dc5..b9954beac 100644 --- a/ruoyi-extend/ruoyi-xxl-job-admin/pom.xml +++ b/ruoyi-extend/ruoyi-xxl-job-admin/pom.xml @@ -4,7 +4,7 @@ ruoyi-extend com.ruoyi - 4.1.0 + 4.2.0 ruoyi-xxl-job-admin jar diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/IndexController.java b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/IndexController.java index 8d5495c53..bbb25077d 100644 --- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/IndexController.java +++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/IndexController.java @@ -56,13 +56,13 @@ public class IndexController { @PermissionLimit(limit = false) public ModelAndView toLogin(HttpServletRequest request, HttpServletResponse response, ModelAndView modelAndView) { if (loginService.ifLogin(request, response) != null) { - modelAndView.setView(new RedirectView("/" , true, false)); + modelAndView.setView(new RedirectView("/", true, false)); return modelAndView; } return new ModelAndView("login"); } - @RequestMapping(value = "login" , method = RequestMethod.POST) + @RequestMapping(value = "login", method = RequestMethod.POST) @ResponseBody @PermissionLimit(limit = false) public ReturnT loginDo(HttpServletRequest request, HttpServletResponse response, String userName, String password, String ifRemember) { @@ -70,7 +70,7 @@ public class IndexController { return loginService.login(request, response, userName, password, ifRem); } - @RequestMapping(value = "logout" , method = RequestMethod.POST) + @RequestMapping(value = "logout", method = RequestMethod.POST) @ResponseBody @PermissionLimit(limit = false) public ReturnT logout(HttpServletRequest request, HttpServletResponse response) { diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobApiController.java b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobApiController.java index aa51e7390..f4a37a71d 100644 --- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobApiController.java +++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobApiController.java @@ -37,19 +37,19 @@ public class JobApiController { */ @RequestMapping("/{uri}") @ResponseBody - @PermissionLimit(limit=false) + @PermissionLimit(limit = false) public ReturnT api(HttpServletRequest request, @PathVariable("uri") String uri, @RequestBody(required = false) String data) { // valid if (!"POST".equalsIgnoreCase(request.getMethod())) { return new ReturnT(ReturnT.FAIL_CODE, "invalid request, HttpMethod not support."); } - if (uri==null || uri.trim().length()==0) { + if (uri == null || uri.trim().length() == 0) { return new ReturnT(ReturnT.FAIL_CODE, "invalid request, uri-mapping empty."); } - if (XxlJobAdminConfig.getAdminConfig().getAccessToken()!=null - && XxlJobAdminConfig.getAdminConfig().getAccessToken().trim().length()>0 - && !XxlJobAdminConfig.getAdminConfig().getAccessToken().equals(request.getHeader(XxlJobRemotingUtil.XXL_JOB_ACCESS_TOKEN))) { + if (XxlJobAdminConfig.getAdminConfig().getAccessToken() != null + && XxlJobAdminConfig.getAdminConfig().getAccessToken().trim().length() > 0 + && !XxlJobAdminConfig.getAdminConfig().getAccessToken().equals(request.getHeader(XxlJobRemotingUtil.XXL_JOB_ACCESS_TOKEN))) { return new ReturnT(ReturnT.FAIL_CODE, "The access token is wrong."); } @@ -64,7 +64,7 @@ public class JobApiController { RegistryParam registryParam = GsonTool.fromJson(data, RegistryParam.class); return adminBiz.registryRemove(registryParam); } else { - return new ReturnT(ReturnT.FAIL_CODE, "invalid request, uri-mapping("+ uri +") not found."); + return new ReturnT(ReturnT.FAIL_CODE, "invalid request, uri-mapping(" + uri + ") not found."); } } diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobCodeController.java b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobCodeController.java index 0c8384f0f..9185f86b4 100644 --- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobCodeController.java +++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobCodeController.java @@ -47,10 +47,10 @@ public class JobCodeController { JobInfoController.validPermission(request, jobInfo.getJobGroup()); // Glue类型-字典 - model.addAttribute("GlueTypeEnum" , GlueTypeEnum.values()); + model.addAttribute("GlueTypeEnum", GlueTypeEnum.values()); - model.addAttribute("jobInfo" , jobInfo); - model.addAttribute("jobLogGlues" , jobLogGlues); + model.addAttribute("jobInfo", jobInfo); + model.addAttribute("jobLogGlues", jobLogGlues); return "jobcode/jobcode.index"; } diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobGroupController.java b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobGroupController.java index 4a4620092..82ec2dee4 100644 --- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobGroupController.java +++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobGroupController.java @@ -52,9 +52,9 @@ public class JobGroupController { // package result Map maps = new HashMap(); - maps.put("recordsTotal" , list_count); // 总记录数 - maps.put("recordsFiltered" , list_count); // 过滤后的总记录数 - maps.put("data" , list); // 分页列表 + maps.put("recordsTotal", list_count); // 总记录数 + maps.put("recordsFiltered", list_count); // 过滤后的总记录数 + maps.put("data", list); // 分页列表 return maps; } diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobInfoController.java b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobInfoController.java index 0f52bef73..157c60ba7 100644 --- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobInfoController.java +++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobInfoController.java @@ -49,11 +49,11 @@ public class JobInfoController { public String index(HttpServletRequest request, Model model, @RequestParam(required = false, defaultValue = "-1") int jobGroup) { // 枚举-字典 - model.addAttribute("ExecutorRouteStrategyEnum" , ExecutorRouteStrategyEnum.values()); // 路由策略-列表 - model.addAttribute("GlueTypeEnum" , GlueTypeEnum.values()); // Glue类型-字典 - model.addAttribute("ExecutorBlockStrategyEnum" , ExecutorBlockStrategyEnum.values()); // 阻塞处理策略-字典 - model.addAttribute("ScheduleTypeEnum" , ScheduleTypeEnum.values()); // 调度类型 - model.addAttribute("MisfireStrategyEnum" , MisfireStrategyEnum.values()); // 调度过期策略 + model.addAttribute("ExecutorRouteStrategyEnum", ExecutorRouteStrategyEnum.values()); // 路由策略-列表 + model.addAttribute("GlueTypeEnum", GlueTypeEnum.values()); // Glue类型-字典 + model.addAttribute("ExecutorBlockStrategyEnum", ExecutorBlockStrategyEnum.values()); // 阻塞处理策略-字典 + model.addAttribute("ScheduleTypeEnum", ScheduleTypeEnum.values()); // 调度类型 + model.addAttribute("MisfireStrategyEnum", MisfireStrategyEnum.values()); // 调度过期策略 // 执行器列表 List jobGroupList_all = xxlJobGroupDao.findAll(); @@ -64,8 +64,8 @@ public class JobInfoController { throw new XxlJobException(I18nUtil.getString("jobgroup_empty")); } - model.addAttribute("JobGroupList" , jobGroupList); - model.addAttribute("jobGroup" , jobGroup); + model.addAttribute("JobGroupList", jobGroupList); + model.addAttribute("jobGroup", jobGroup); return "jobinfo/jobinfo.index"; } diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobLogController.java b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobLogController.java index 141b539c1..3369edb8f 100644 --- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobLogController.java +++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobLogController.java @@ -1,7 +1,7 @@ package com.xxl.job.admin.controller; -import com.xxl.job.admin.core.complete.XxlJobCompleter; import com.xxl.job.admin.core.exception.XxlJobException; +import com.xxl.job.admin.core.complete.XxlJobCompleter; import com.xxl.job.admin.core.model.XxlJobGroup; import com.xxl.job.admin.core.model.XxlJobInfo; import com.xxl.job.admin.core.model.XxlJobLog; @@ -60,7 +60,7 @@ public class JobLogController { throw new XxlJobException(I18nUtil.getString("jobgroup_empty")); } - model.addAttribute("JobGroupList" , jobGroupList); + model.addAttribute("JobGroupList", jobGroupList); // 任务 if (jobId > 0) { @@ -69,7 +69,7 @@ public class JobLogController { throw new RuntimeException(I18nUtil.getString("jobinfo_field_id") + I18nUtil.getString("system_unvalid")); } - model.addAttribute("jobInfo" , jobInfo); + model.addAttribute("jobInfo", jobInfo); // valid permission JobInfoController.validPermission(request, jobInfo.getJobGroup()); @@ -112,9 +112,9 @@ public class JobLogController { // package result Map maps = new HashMap(); - maps.put("recordsTotal" , list_count); // 总记录数 - maps.put("recordsFiltered" , list_count); // 过滤后的总记录数 - maps.put("data" , list); // 分页列表 + maps.put("recordsTotal", list_count); // 总记录数 + maps.put("recordsFiltered", list_count); // 过滤后的总记录数 + maps.put("data", list); // 分页列表 return maps; } @@ -128,11 +128,11 @@ public class JobLogController { throw new RuntimeException(I18nUtil.getString("joblog_logid_unvalid")); } - model.addAttribute("triggerCode" , jobLog.getTriggerCode()); - model.addAttribute("handleCode" , jobLog.getHandleCode()); - model.addAttribute("executorAddress" , jobLog.getExecutorAddress()); - model.addAttribute("triggerTime" , jobLog.getTriggerTime().getTime()); - model.addAttribute("logId" , jobLog.getId()); + model.addAttribute("triggerCode", jobLog.getTriggerCode()); + model.addAttribute("handleCode", jobLog.getHandleCode()); + model.addAttribute("executorAddress", jobLog.getExecutorAddress()); + model.addAttribute("triggerTime", jobLog.getTriggerTime().getTime()); + model.addAttribute("logId", jobLog.getId()); return "joblog/joblog.detail"; } diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/UserController.java b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/UserController.java index b38e1b1b8..878998d39 100644 --- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/UserController.java +++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/UserController.java @@ -40,7 +40,7 @@ public class UserController { // 执行器列表 List groupList = xxlJobGroupDao.findAll(); - model.addAttribute("groupList" , groupList); + model.addAttribute("groupList", groupList); return "user/user.index"; } @@ -65,9 +65,9 @@ public class UserController { // package result Map maps = new HashMap(); - maps.put("recordsTotal" , list_count); // 总记录数 - maps.put("recordsFiltered" , list_count); // 过滤后的总记录数 - maps.put("data" , list); // 分页列表 + maps.put("recordsTotal", list_count); // 总记录数 + maps.put("recordsFiltered", list_count); // 过滤后的总记录数 + maps.put("data", list); // 分页列表 return maps; } diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/CookieInterceptor.java b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/CookieInterceptor.java index 6714d1eca..228384996 100644 --- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/CookieInterceptor.java +++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/CookieInterceptor.java @@ -29,15 +29,14 @@ public class CookieInterceptor implements AsyncHandlerInterceptor { for (Cookie ck : request.getCookies()) { cookieMap.put(ck.getName(), ck); } - modelAndView.addObject("cookieMap" , cookieMap); + modelAndView.addObject("cookieMap", cookieMap); } // static method if (modelAndView != null) { - modelAndView.addObject("I18nUtil" , FtlUtil.generateStaticModel(I18nUtil.class.getName())); + modelAndView.addObject("I18nUtil", FtlUtil.generateStaticModel(I18nUtil.class.getName())); } - AsyncHandlerInterceptor.super.postHandle(request, response, handler, modelAndView); } } diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/PermissionInterceptor.java b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/PermissionInterceptor.java index ecaeca0ff..13e53b2f1 100644 --- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/PermissionInterceptor.java +++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/PermissionInterceptor.java @@ -27,7 +27,7 @@ public class PermissionInterceptor implements AsyncHandlerInterceptor { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (!(handler instanceof HandlerMethod)) { - return AsyncHandlerInterceptor.super.preHandle(request, response, handler); + return true; // proceed with the next interceptor } // if need login @@ -44,7 +44,7 @@ public class PermissionInterceptor implements AsyncHandlerInterceptor { XxlJobUser loginUser = loginService.ifLogin(request, response); if (loginUser == null) { response.setStatus(302); - response.setHeader("location" , request.getContextPath() + "/toLogin"); + response.setHeader("location", request.getContextPath() + "/toLogin"); return false; } if (needAdminuser && loginUser.getRole() != 1) { @@ -53,7 +53,7 @@ public class PermissionInterceptor implements AsyncHandlerInterceptor { request.setAttribute(LoginService.LOGIN_IDENTITY_KEY, loginUser); } - return AsyncHandlerInterceptor.super.preHandle(request, response, handler); + return true; // proceed with the next interceptor } } diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/resolver/WebExceptionResolver.java b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/resolver/WebExceptionResolver.java index 4cd7f193d..d7cc0db5e 100644 --- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/resolver/WebExceptionResolver.java +++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/resolver/WebExceptionResolver.java @@ -1,8 +1,8 @@ package com.xxl.job.admin.controller.resolver; import com.xxl.job.admin.core.exception.XxlJobException; -import com.xxl.job.admin.core.util.JacksonUtil; import com.xxl.job.core.biz.model.ReturnT; +import com.xxl.job.admin.core.util.JacksonUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @@ -29,7 +29,7 @@ public class WebExceptionResolver implements HandlerExceptionResolver { HttpServletResponse response, Object handler, Exception ex) { if (!(ex instanceof XxlJobException)) { - logger.error("WebExceptionResolver:{}" , ex); + logger.error("WebExceptionResolver:{}", ex); } // if json @@ -43,7 +43,7 @@ public class WebExceptionResolver implements HandlerExceptionResolver { } // error result - ReturnT errorResult = new ReturnT(ReturnT.FAIL_CODE, ex.toString().replaceAll("\n" , "
")); + ReturnT errorResult = new ReturnT(ReturnT.FAIL_CODE, ex.toString().replaceAll("\n", "
")); // response ModelAndView mv = new ModelAndView(); @@ -57,7 +57,7 @@ public class WebExceptionResolver implements HandlerExceptionResolver { return mv; } else { - mv.addObject("exceptionMsg" , errorResult.getMsg()); + mv.addObject("exceptionMsg", errorResult.getMsg()); mv.setViewName("/common/common.exception"); return mv; } diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/alarm/JobAlarmer.java b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/alarm/JobAlarmer.java index 797dc900c..62dac9d27 100644 --- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/alarm/JobAlarmer.java +++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/alarm/JobAlarmer.java @@ -44,9 +44,9 @@ public class JobAlarmer implements ApplicationContextAware, InitializingBean { public boolean alarm(XxlJobInfo info, XxlJobLog jobLog) { boolean result = false; - if (jobAlarmList!=null && jobAlarmList.size()>0) { + if (jobAlarmList != null && jobAlarmList.size() > 0) { result = true; // success means all-success - for (JobAlarm alarm: jobAlarmList) { + for (JobAlarm alarm : jobAlarmList) { boolean resultItem = false; try { resultItem = alarm.doAlarm(info, jobLog); diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/alarm/impl/EmailJobAlarm.java b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/alarm/impl/EmailJobAlarm.java index e7290d76f..6ad1c0b1c 100644 --- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/alarm/impl/EmailJobAlarm.java +++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/alarm/impl/EmailJobAlarm.java @@ -32,18 +32,19 @@ public class EmailJobAlarm implements JobAlarm { * * @param jobLog */ - public boolean doAlarm(XxlJobInfo info, XxlJobLog jobLog){ + @Override + public boolean doAlarm(XxlJobInfo info, XxlJobLog jobLog) { boolean alarmResult = true; // send monitor email - if (info!=null && info.getAlarmEmail()!=null && info.getAlarmEmail().trim().length()>0) { + if (info != null && info.getAlarmEmail() != null && info.getAlarmEmail().trim().length() > 0) { // alarmContent String alarmContent = "Alarm Job LogId=" + jobLog.getId(); if (jobLog.getTriggerCode() != ReturnT.SUCCESS_CODE) { alarmContent += "
TriggerMsg=
" + jobLog.getTriggerMsg(); } - if (jobLog.getHandleCode()>0 && jobLog.getHandleCode() != ReturnT.SUCCESS_CODE) { + if (jobLog.getHandleCode() > 0 && jobLog.getHandleCode() != ReturnT.SUCCESS_CODE) { alarmContent += "
HandleCode=" + jobLog.getHandleMsg(); } @@ -52,13 +53,13 @@ public class EmailJobAlarm implements JobAlarm { String personal = I18nUtil.getString("admin_name_full"); String title = I18nUtil.getString("jobconf_monitor"); String content = MessageFormat.format(loadEmailJobAlarmTemplate(), - group!=null?group.getTitle():"null", - info.getId(), - info.getJobDesc(), - alarmContent); + group != null ? group.getTitle() : "null", + info.getId(), + info.getJobDesc(), + alarmContent); Set emailSet = new HashSet(Arrays.asList(info.getAlarmEmail().split(","))); - for (String email: emailSet) { + for (String email : emailSet) { // make mail try { @@ -88,28 +89,28 @@ public class EmailJobAlarm implements JobAlarm { * * @return */ - private static final String loadEmailJobAlarmTemplate(){ + private static final String loadEmailJobAlarmTemplate() { String mailBodyTemplate = "
" + I18nUtil.getString("jobconf_monitor_detail") + ":" + - "\n" + - " " + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - "
"+ I18nUtil.getString("jobinfo_field_jobgroup") +""+ I18nUtil.getString("jobinfo_field_id") +""+ I18nUtil.getString("jobinfo_field_jobdesc") +""+ I18nUtil.getString("jobconf_monitor_alarm_title") +""+ I18nUtil.getString("jobconf_monitor_alarm_content") +"
{0}{1}{2}"+ I18nUtil.getString("jobconf_monitor_alarm_type") +"{3}
"; + "\n" + + " " + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
" + I18nUtil.getString("jobinfo_field_jobgroup") + "" + I18nUtil.getString("jobinfo_field_id") + "" + I18nUtil.getString("jobinfo_field_jobdesc") + "" + I18nUtil.getString("jobconf_monitor_alarm_title") + "" + I18nUtil.getString("jobconf_monitor_alarm_content") + "
{0}{1}{2}" + I18nUtil.getString("jobconf_monitor_alarm_type") + "{3}
"; return mailBodyTemplate; } diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/complete/XxlJobCompleter.java b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/complete/XxlJobCompleter.java index b9ac59a38..83399336b 100644 --- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/complete/XxlJobCompleter.java +++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/complete/XxlJobCompleter.java @@ -32,7 +32,7 @@ public class XxlJobCompleter { // text最大64kb 避免长度过长 if (xxlJobLog.getHandleMsg().length() > 15000) { - xxlJobLog.setHandleMsg( xxlJobLog.getHandleMsg().substring(0, 15000) ); + xxlJobLog.setHandleMsg(xxlJobLog.getHandleMsg().substring(0, 15000)); } // fresh handle @@ -43,18 +43,18 @@ public class XxlJobCompleter { /** * do somethind to finish job */ - private static void finishJob(XxlJobLog xxlJobLog){ + private static void finishJob(XxlJobLog xxlJobLog) { // 1、handle success, to trigger child job String triggerChildMsg = null; - if (XxlJobContext.HANDLE_COCE_SUCCESS == xxlJobLog.getHandleCode()) { + if (XxlJobContext.HANDLE_CODE_SUCCESS == xxlJobLog.getHandleCode()) { XxlJobInfo xxlJobInfo = XxlJobAdminConfig.getAdminConfig().getXxlJobInfoDao().loadById(xxlJobLog.getJobId()); - if (xxlJobInfo!=null && xxlJobInfo.getChildJobId()!=null && xxlJobInfo.getChildJobId().trim().length()>0) { - triggerChildMsg = "

>>>>>>>>>>>"+ I18nUtil.getString("jobconf_trigger_child_run") +"<<<<<<<<<<<
"; + if (xxlJobInfo != null && xxlJobInfo.getChildJobId() != null && xxlJobInfo.getChildJobId().trim().length() > 0) { + triggerChildMsg = "

>>>>>>>>>>>" + I18nUtil.getString("jobconf_trigger_child_run") + "<<<<<<<<<<<
"; String[] childJobIds = xxlJobInfo.getChildJobId().split(","); for (int i = 0; i < childJobIds.length; i++) { - int childJobId = (childJobIds[i]!=null && childJobIds[i].trim().length()>0 && isNumeric(childJobIds[i]))?Integer.valueOf(childJobIds[i]):-1; + int childJobId = (childJobIds[i] != null && childJobIds[i].trim().length() > 0 && isNumeric(childJobIds[i])) ? Integer.valueOf(childJobIds[i]) : -1; if (childJobId > 0) { JobTriggerPoolHelper.trigger(childJobId, TriggerTypeEnum.PARENT, -1, null, null, null); @@ -62,16 +62,16 @@ public class XxlJobCompleter { // add msg triggerChildMsg += MessageFormat.format(I18nUtil.getString("jobconf_callback_child_msg1"), - (i+1), - childJobIds.length, - childJobIds[i], - (triggerChildResult.getCode()==ReturnT.SUCCESS_CODE?I18nUtil.getString("system_success"):I18nUtil.getString("system_fail")), - triggerChildResult.getMsg()); + (i + 1), + childJobIds.length, + childJobIds[i], + (triggerChildResult.getCode() == ReturnT.SUCCESS_CODE ? I18nUtil.getString("system_success") : I18nUtil.getString("system_fail")), + triggerChildResult.getMsg()); } else { triggerChildMsg += MessageFormat.format(I18nUtil.getString("jobconf_callback_child_msg2"), - (i+1), - childJobIds.length, - childJobIds[i]); + (i + 1), + childJobIds.length, + childJobIds[i]); } } @@ -79,7 +79,7 @@ public class XxlJobCompleter { } if (triggerChildMsg != null) { - xxlJobLog.setHandleMsg( xxlJobLog.getHandleMsg() + triggerChildMsg ); + xxlJobLog.setHandleMsg(xxlJobLog.getHandleMsg() + triggerChildMsg); } // 2、fix_delay trigger next @@ -87,7 +87,7 @@ public class XxlJobCompleter { } - private static boolean isNumeric(String str){ + private static boolean isNumeric(String str) { try { int result = Integer.valueOf(str); return true; diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/conf/XxlJobAdminConfig.java b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/conf/XxlJobAdminConfig.java index 380b8a596..6e40cb760 100644 --- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/conf/XxlJobAdminConfig.java +++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/conf/XxlJobAdminConfig.java @@ -23,6 +23,7 @@ import java.util.Arrays; public class XxlJobAdminConfig implements InitializingBean, DisposableBean { private static XxlJobAdminConfig adminConfig = null; + public static XxlJobAdminConfig getAdminConfig() { return adminConfig; } diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/cron/CronExpression.java b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/cron/CronExpression.java index fce23524d..2ce373eeb 100644 --- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/cron/CronExpression.java +++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/cron/CronExpression.java @@ -1,18 +1,18 @@ /* * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations * under the License. - * + * */ package com.xxl.job.admin.core.cron; @@ -31,14 +31,14 @@ import java.util.TimeZone; import java.util.TreeSet; /** - * Provides a parser and evaluator for unix-like cron expressions. Cron + * Provides a parser and evaluator for unix-like cron expressions. Cron * expressions provide the ability to specify complex time combinations such as - * "At 8:00am every Monday through Friday" or "At 1:30am every - * last Friday of the month". + * "At 8:00am every Monday through Friday" or "At 1:30am every + * last Friday of the month". *

* Cron expressions are comprised of 6 required fields and one optional field * separated by white space. The fields respectively are described as follows: - * + * * * * @@ -98,7 +98,7 @@ import java.util.TreeSet; * *
Field Name
*

- * The '*' character is used to specify all values. For example, "*" + * The '*' character is used to specify all values. For example, "*" * in the minute field means "every minute". *

* The '?' character is allowed for the day-of-month and day-of-week fields. It @@ -113,55 +113,55 @@ import java.util.TreeSet; * Wednesday, and Friday". *

* The '/' character is used to specify increments. For example "0/15" - * in the seconds field means "the seconds 0, 15, 30, and 45". And + * in the seconds field means "the seconds 0, 15, 30, and 45". And * "5/15" in the seconds field means "the seconds 5, 20, 35, and * 50". Specifying '*' before the '/' is equivalent to specifying 0 is * the value to start with. Essentially, for each field in the expression, there - * is a set of numbers that can be turned on or off. For seconds and minutes, + * is a set of numbers that can be turned on or off. For seconds and minutes, * the numbers range from 0 to 59. For hours 0 to 23, for days of the month 0 to * 31, and for months 0 to 11 (JAN to DEC). The "/" character simply helps you turn * on every "nth" value in the given set. Thus "7/6" in the - * month field only turns on month "7", it does NOT mean every 6th - * month, please note that subtlety. + * month field only turns on month "7", it does NOT mean every 6th + * month, please note that subtlety. *

* The 'L' character is allowed for the day-of-month and day-of-week fields. - * This character is short-hand for "last", but it has different - * meaning in each of the two fields. For example, the value "L" in - * the day-of-month field means "the last day of the month" - day 31 - * for January, day 28 for February on non-leap years. If used in the - * day-of-week field by itself, it simply means "7" or + * This character is short-hand for "last", but it has different + * meaning in each of the two fields. For example, the value "L" in + * the day-of-month field means "the last day of the month" - day 31 + * for January, day 28 for February on non-leap years. If used in the + * day-of-week field by itself, it simply means "7" or * "SAT". But if used in the day-of-week field after another value, it * means "the last xxx day of the month" - for example "6L" - * means "the last friday of the month". You can also specify an offset - * from the last day of the month, such as "L-3" which would mean the third-to-last - * day of the calendar month. When using the 'L' option, it is important not to + * means "the last friday of the month". You can also specify an offset + * from the last day of the month, such as "L-3" which would mean the third-to-last + * day of the calendar month. When using the 'L' option, it is important not to * specify lists, or ranges of values, as you'll get confusing/unexpected results. *

- * The 'W' character is allowed for the day-of-month field. This character - * is used to specify the weekday (Monday-Friday) nearest the given day. As an - * example, if you were to specify "15W" as the value for the + * The 'W' character is allowed for the day-of-month field. This character + * is used to specify the weekday (Monday-Friday) nearest the given day. As an + * example, if you were to specify "15W" as the value for the * day-of-month field, the meaning is: "the nearest weekday to the 15th of - * the month". So if the 15th is a Saturday, the trigger will fire on + * the month". So if the 15th is a Saturday, the trigger will fire on * Friday the 14th. If the 15th is a Sunday, the trigger will fire on Monday the - * 16th. If the 15th is a Tuesday, then it will fire on Tuesday the 15th. + * 16th. If the 15th is a Tuesday, then it will fire on Tuesday the 15th. * However if you specify "1W" as the value for day-of-month, and the - * 1st is a Saturday, the trigger will fire on Monday the 3rd, as it will not - * 'jump' over the boundary of a month's days. The 'W' character can only be + * 1st is a Saturday, the trigger will fire on Monday the 3rd, as it will not + * 'jump' over the boundary of a month's days. The 'W' character can only be * specified when the day-of-month is a single day, not a range or list of days. *

- * The 'L' and 'W' characters can also be combined for the day-of-month - * expression to yield 'LW', which translates to "last weekday of the + * The 'L' and 'W' characters can also be combined for the day-of-month + * expression to yield 'LW', which translates to "last weekday of the * month". *

* The '#' character is allowed for the day-of-week field. This character is - * used to specify "the nth" XXX day of the month. For example, the - * value of "6#3" in the day-of-week field means the third Friday of - * the month (day 6 = Friday and "#3" = the 3rd one in the month). - * Other examples: "2#1" = the first Monday of the month and + * used to specify "the nth" XXX day of the month. For example, the + * value of "6#3" in the day-of-week field means the third Friday of + * the month (day 6 = Friday and "#3" = the 3rd one in the month). + * Other examples: "2#1" = the first Monday of the month and * "4#5" = the fifth Wednesday of the month. Note that if you specify * "#5" and there is not 5 of the given day-of-week in the month, then * no firing will occur that month. If the '#' character is used, there can - * only be one expression in the day-of-week field ("3#1,6#3" is + * only be one expression in the day-of-week field ("3#1,6#3" is * not valid, since there are two expressions). *

* + + com.ruoyi + ruoyi-common + + + + com.aliyun + dysmsapi20170525 + true + + + + com.tencentcloudapi + tencentcloud-sdk-java + true + + + + + diff --git a/ruoyi-sms/src/main/java/com/ruoyi/sms/config/SmsConfig.java b/ruoyi-sms/src/main/java/com/ruoyi/sms/config/SmsConfig.java new file mode 100644 index 000000000..753773e87 --- /dev/null +++ b/ruoyi-sms/src/main/java/com/ruoyi/sms/config/SmsConfig.java @@ -0,0 +1,45 @@ +package com.ruoyi.sms.config; + +import com.ruoyi.sms.config.properties.SmsProperties; +import com.ruoyi.sms.core.AliyunSmsTemplate; +import com.ruoyi.sms.core.SmsTemplate; +import com.ruoyi.sms.core.TencentSmsTemplate; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * 短信配置类 + * + * @author Lion Li + * @version 4.2.0 + */ +@Configuration +public class SmsConfig { + + @Configuration + @ConditionalOnProperty(value = "sms.enabled", havingValue = "true") + @ConditionalOnClass(com.aliyun.dysmsapi20170525.Client.class) + static class AliyunSmsConfig { + + @Bean + public SmsTemplate aliyunSmsTemplate(SmsProperties smsProperties) { + return new AliyunSmsTemplate(smsProperties); + } + + } + + @Configuration + @ConditionalOnProperty(value = "sms.enabled", havingValue = "true") + @ConditionalOnClass(com.tencentcloudapi.sms.v20190711.SmsClient.class) + static class TencentSmsConfig { + + @Bean + public SmsTemplate tencentSmsTemplate(SmsProperties smsProperties) { + return new TencentSmsTemplate(smsProperties); + } + + } + +} diff --git a/ruoyi-sms/src/main/java/com/ruoyi/sms/config/properties/SmsProperties.java b/ruoyi-sms/src/main/java/com/ruoyi/sms/config/properties/SmsProperties.java new file mode 100644 index 000000000..39359cdfd --- /dev/null +++ b/ruoyi-sms/src/main/java/com/ruoyi/sms/config/properties/SmsProperties.java @@ -0,0 +1,47 @@ +package com.ruoyi.sms.config.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * SMS短信 配置属性 + * + * @author Lion Li + * @version 4.2.0 + */ +@Data +@Component +@ConfigurationProperties(prefix = "sms") +public class SmsProperties { + + private Boolean enabled; + + /** + * 配置节点 + * 阿里云 dysmsapi.aliyuncs.com + * 腾讯云 sms.tencentcloudapi.com + */ + private String endpoint; + + /** + * key + */ + private String accessKeyId; + + /** + * 密匙 + */ + private String accessKeySecret; + + /* + * 短信签名 + */ + private String signName; + + /** + * 短信应用ID (腾讯专属) + */ + private String sdkAppId; + +} diff --git a/ruoyi-sms/src/main/java/com/ruoyi/sms/core/AliyunSmsTemplate.java b/ruoyi-sms/src/main/java/com/ruoyi/sms/core/AliyunSmsTemplate.java new file mode 100644 index 000000000..87c3f3bdb --- /dev/null +++ b/ruoyi-sms/src/main/java/com/ruoyi/sms/core/AliyunSmsTemplate.java @@ -0,0 +1,65 @@ +package com.ruoyi.sms.core; + +import com.aliyun.dysmsapi20170525.Client; +import com.aliyun.dysmsapi20170525.models.SendSmsRequest; +import com.aliyun.dysmsapi20170525.models.SendSmsResponse; +import com.aliyun.teaopenapi.models.Config; +import com.ruoyi.common.utils.JsonUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.sms.config.properties.SmsProperties; +import com.ruoyi.sms.entity.SmsResult; +import com.ruoyi.sms.exception.SmsException; +import lombok.SneakyThrows; + +import java.util.Map; + +/** + * Aliyun 短信模板 + * + * @author Lion Li + * @version 4.2.0 + */ +public class AliyunSmsTemplate implements SmsTemplate { + + private SmsProperties properties; + + private Client client; + + @SneakyThrows(Exception.class) + public AliyunSmsTemplate(SmsProperties smsProperties) { + this.properties = smsProperties; + Config config = new Config() + // 您的AccessKey ID + .setAccessKeyId(smsProperties.getAccessKeyId()) + // 您的AccessKey Secret + .setAccessKeySecret(smsProperties.getAccessKeySecret()) + // 访问的域名 + .setEndpoint(smsProperties.getEndpoint()); + this.client = new Client(config); + } + + public SmsResult send(String phones, String templateId, Map param) { + if (StringUtils.isBlank(phones)) { + throw new SmsException("手机号不能为空"); + } + if (StringUtils.isBlank(templateId)) { + throw new SmsException("模板ID不能为空"); + } + SendSmsRequest req = new SendSmsRequest() + .setPhoneNumbers(phones) + .setSignName(properties.getSignName()) + .setTemplateCode(templateId) + .setTemplateParam(JsonUtils.toJsonString(param)); + try { + SendSmsResponse resp = client.sendSms(req); + return SmsResult.builder() + .isSuccess("OK".equals(resp.getBody().getCode())) + .message(resp.getBody().getMessage()) + .response(resp) + .build(); + } catch (Exception e) { + throw new SmsException(e.getMessage()); + } + } + +} diff --git a/ruoyi-sms/src/main/java/com/ruoyi/sms/core/SmsTemplate.java b/ruoyi-sms/src/main/java/com/ruoyi/sms/core/SmsTemplate.java new file mode 100644 index 000000000..0aec3ddbe --- /dev/null +++ b/ruoyi-sms/src/main/java/com/ruoyi/sms/core/SmsTemplate.java @@ -0,0 +1,26 @@ +package com.ruoyi.sms.core; + +import com.ruoyi.sms.entity.SmsResult; + +import java.util.Map; + +/** + * 短信模板 + * + * @author Lion Li + * @version 4.2.0 + */ +public interface SmsTemplate { + + /** + * 发送短信 + * + * @param phones 电话号(多个逗号分割) + * @param templateId 模板id + * @param param 模板对应参数 + * 阿里 需使用 模板变量名称对应内容 例如: code=1234 + * 腾讯 需使用 模板变量顺序对应内容 例如: 1=1234, 1为模板内第一个参数 + */ + SmsResult send(String phones, String templateId, Map param); + +} diff --git a/ruoyi-sms/src/main/java/com/ruoyi/sms/core/TencentSmsTemplate.java b/ruoyi-sms/src/main/java/com/ruoyi/sms/core/TencentSmsTemplate.java new file mode 100644 index 000000000..ce82c3c69 --- /dev/null +++ b/ruoyi-sms/src/main/java/com/ruoyi/sms/core/TencentSmsTemplate.java @@ -0,0 +1,80 @@ +package com.ruoyi.sms.core; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ArrayUtil; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.sms.config.properties.SmsProperties; +import com.ruoyi.sms.entity.SmsResult; +import com.ruoyi.sms.exception.SmsException; +import com.tencentcloudapi.common.Credential; +import com.tencentcloudapi.common.profile.ClientProfile; +import com.tencentcloudapi.common.profile.HttpProfile; +import com.tencentcloudapi.sms.v20190711.SmsClient; +import com.tencentcloudapi.sms.v20190711.models.SendSmsRequest; +import com.tencentcloudapi.sms.v20190711.models.SendSmsResponse; +import com.tencentcloudapi.sms.v20190711.models.SendStatus; +import lombok.SneakyThrows; + +import java.util.Arrays; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Tencent 短信模板 + * + * @author Lion Li + * @version 4.2.0 + */ +public class TencentSmsTemplate implements SmsTemplate { + + private SmsProperties properties; + + private SmsClient client; + + @SneakyThrows(Exception.class) + public TencentSmsTemplate(SmsProperties smsProperties) { + this.properties = smsProperties; + Credential credential = new Credential(smsProperties.getAccessKeyId(), smsProperties.getAccessKeySecret()); + HttpProfile httpProfile = new HttpProfile(); + httpProfile.setEndpoint(smsProperties.getEndpoint()); + ClientProfile clientProfile = new ClientProfile(); + clientProfile.setHttpProfile(httpProfile); + this.client = new SmsClient(credential, "", clientProfile); + } + + public SmsResult send(String phones, String templateId, Map param) { + if (StringUtils.isBlank(phones)) { + throw new SmsException("手机号不能为空"); + } + if (StringUtils.isBlank(templateId)) { + throw new SmsException("模板ID不能为空"); + } + SendSmsRequest req = new SendSmsRequest(); + Set set = Arrays.stream(phones.split(",")).map(p -> "+86" + p).collect(Collectors.toSet()); + req.setPhoneNumberSet(ArrayUtil.toArray(set, String.class)); + if (CollUtil.isNotEmpty(param)) { + req.setTemplateParamSet(ArrayUtil.toArray(param.values(), String.class)); + } + req.setTemplateID(templateId); + req.setSign(properties.getSignName()); + req.setSmsSdkAppid(properties.getSdkAppId()); + try { + SendSmsResponse resp = client.SendSms(req); + SmsResult.SmsResultBuilder builder = SmsResult.builder() + .isSuccess(true) + .message("send success") + .response(resp); + for (SendStatus sendStatus : resp.getSendStatusSet()) { + if (!"Ok".equals(sendStatus.getCode())) { + builder.isSuccess(false).message(sendStatus.getMessage()); + break; + } + } + return builder.build(); + } catch (Exception e) { + throw new SmsException(e.getMessage()); + } + } + +} diff --git a/ruoyi-sms/src/main/java/com/ruoyi/sms/entity/SmsResult.java b/ruoyi-sms/src/main/java/com/ruoyi/sms/entity/SmsResult.java new file mode 100644 index 000000000..3f13b2774 --- /dev/null +++ b/ruoyi-sms/src/main/java/com/ruoyi/sms/entity/SmsResult.java @@ -0,0 +1,29 @@ +package com.ruoyi.sms.entity; + +import lombok.Builder; +import lombok.Data; + +/** + * 上传返回体 + * + * @author Lion Li + */ +@Data +@Builder +public class SmsResult { + + /** + * 是否成功 + */ + private boolean isSuccess; + + /** + * 响应消息 + */ + private String message; + + /** + * 实际响应体 + */ + private Object response; +} diff --git a/ruoyi-sms/src/main/java/com/ruoyi/sms/exception/SmsException.java b/ruoyi-sms/src/main/java/com/ruoyi/sms/exception/SmsException.java new file mode 100644 index 000000000..28632a375 --- /dev/null +++ b/ruoyi-sms/src/main/java/com/ruoyi/sms/exception/SmsException.java @@ -0,0 +1,16 @@ +package com.ruoyi.sms.exception; + +/** + * Sms异常类 + * + * @author Lion Li + */ +public class SmsException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public SmsException(String msg) { + super(msg); + } + +} diff --git a/ruoyi-system/pom.xml b/ruoyi-system/pom.xml index 57ec06c08..9e49b71ad 100644 --- a/ruoyi-system/pom.xml +++ b/ruoyi-system/pom.xml @@ -5,7 +5,7 @@ ruoyi-vue-plus com.ruoyi - 4.1.0 + 4.2.0 4.0.0 @@ -29,6 +29,12 @@ ruoyi-oss + + + com.ruoyi + ruoyi-sms + + diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOssConfig.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOssConfig.java index a8340df77..577f17fb8 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOssConfig.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOssConfig.java @@ -5,7 +5,6 @@ import com.baomidou.mybatisplus.annotation.TableName; import com.ruoyi.common.core.domain.BaseEntity; import lombok.Data; import lombok.EqualsAndHashCode; -import lombok.experimental.Accessors; /** * 对象存储配置对象 sys_oss_config @@ -53,6 +52,11 @@ public class SysOssConfig extends BaseEntity { */ private String endpoint; + /** + * 自定义域名 + */ + private String domain; + /** * 是否https(0否 1是) */ diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOssConfigBo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOssConfigBo.java index 9a66e384b..5ac4e96e4 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOssConfigBo.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOssConfigBo.java @@ -35,8 +35,8 @@ public class SysOssConfigBo extends BaseEntity { /** * 配置key */ - @ApiModelProperty(value = "configKey", required = true) - @NotBlank(message = "configKey不能为空", groups = {AddGroup.class, EditGroup.class}) + @ApiModelProperty(value = "配置key", required = true) + @NotBlank(message = "配置key不能为空", groups = {AddGroup.class, EditGroup.class}) @Size(min = 2, max = 100, message = "configKey长度必须介于2和20 之间") private String configKey; @@ -59,8 +59,8 @@ public class SysOssConfigBo extends BaseEntity { /** * 桶名称 */ - @ApiModelProperty(value = "bucketName", required = true) - @NotBlank(message = "bucketName不能为空", groups = {AddGroup.class, EditGroup.class}) + @ApiModelProperty(value = "桶名称", required = true) + @NotBlank(message = "桶名称不能为空", groups = {AddGroup.class, EditGroup.class}) @Size(min = 2, max = 100, message = "bucketName长度必须介于2和100之间") private String bucketName; @@ -73,11 +73,17 @@ public class SysOssConfigBo extends BaseEntity { /** * 访问站点 */ - @ApiModelProperty(value = "endpoint", required = true) - @NotBlank(message = "endpoint不能为空", groups = {AddGroup.class, EditGroup.class}) + @ApiModelProperty(value = "访问站点", required = true) + @NotBlank(message = "访问站点不能为空", groups = {AddGroup.class, EditGroup.class}) @Size(min = 2, max = 100, message = "endpoint长度必须介于2和100之间") private String endpoint; + /** + * 自定义域名 + */ + @ApiModelProperty("自定义域名") + private String domain; + /** * 是否https(Y=是,N=否) */ @@ -93,7 +99,7 @@ public class SysOssConfigBo extends BaseEntity { /** * 域 */ - @ApiModelProperty(value = "region") + @ApiModelProperty(value = "域") private String region; /** @@ -102,4 +108,10 @@ public class SysOssConfigBo extends BaseEntity { @ApiModelProperty(value = "扩展字段") private String ext1; + /** + * 备注 + */ + @ApiModelProperty(value = "备注") + private String remark; + } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysOssConfigVo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysOssConfigVo.java index 0fb08dd00..20edacaa6 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysOssConfigVo.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysOssConfigVo.java @@ -62,6 +62,12 @@ public class SysOssConfigVo { @ApiModelProperty("访问站点") private String endpoint; + /** + * 自定义域名 + */ + @ApiModelProperty("自定义域名") + private String domain; + /** * 是否https(Y=是,N=否) */ diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOssService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOssService.java index b444e6d06..c55e5bc12 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOssService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOssService.java @@ -8,6 +8,7 @@ import com.ruoyi.system.domain.vo.SysOssVo; import org.springframework.web.multipart.MultipartFile; import java.util.Collection; +import java.util.List; /** * 文件上传 服务层 @@ -18,6 +19,8 @@ public interface ISysOssService { TableDataInfo queryPageList(SysOssBo sysOss, PageQuery pageQuery); + List listByIds(Collection ossIds); + SysOss getById(Long ossId); SysOss upload(MultipartFile file); diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java index e0b53c1fb..1a672e59c 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java @@ -27,8 +27,8 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletRequest; +import java.time.Duration; import java.util.List; -import java.util.concurrent.TimeUnit; import java.util.function.Supplier; /** @@ -79,7 +79,7 @@ public class SysLoginService { SysUser user = loadUserByPhonenumber(phonenumber); HttpServletRequest request = ServletUtils.getRequest(); - checkLogin(LoginType.SMS, user.getUserName(), () -> !validateSmsCode(phonenumber, smsCode)); + checkLogin(LoginType.SMS, user.getUserName(), () -> !validateSmsCode(phonenumber, smsCode, request)); // 此处可根据登录用户的数据不同 自行创建 loginUser LoginUser loginUser = buildLoginUser(user); // 生成token @@ -121,9 +121,13 @@ public class SysLoginService { /** * 校验短信验证码 */ - private boolean validateSmsCode(String phonenumber, String smsCode) { - // todo 此处使用手机号查询redis验证码与参数验证码是否一致 用户自行实现 - return true; + private boolean validateSmsCode(String phonenumber, String smsCode, HttpServletRequest request) { + String code = RedisUtils.getCacheObject(Constants.CAPTCHA_CODE_KEY + phonenumber); + if (StringUtils.isBlank(code)) { + asyncService.recordLogininfor(phonenumber, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"), request); + throw new CaptchaExpireException(); + } + return code.equals(smsCode); } /** @@ -205,7 +209,7 @@ public class SysLoginService { loginUser.setUserType(user.getUserType()); loginUser.setMenuPermission(permissionService.getMenuPermission(user)); loginUser.setRolePermission(permissionService.getRolePermission(user)); - loginUser.setDeptName(user.getDept().getDeptName()); + loginUser.setDeptName(ObjectUtil.isNull(user.getDept()) ? "" : user.getDept().getDeptName()); List roles = BeanUtil.copyToList(user.getRoles(), RoleDTO.class); loginUser.setRoles(roles); return loginUser; @@ -248,7 +252,7 @@ public class SysLoginService { errorNumber = ObjectUtil.isNull(errorNumber) ? 1 : errorNumber + 1; // 达到规定错误次数 则锁定登录 if (errorNumber.equals(setErrorNumber)) { - RedisUtils.setCacheObject(errorKey, errorNumber, errorLimitTime, TimeUnit.MINUTES); + RedisUtils.setCacheObject(errorKey, errorNumber, Duration.ofMinutes(errorLimitTime)); asyncService.recordLogininfor(username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), errorLimitTime), request); throw new UserException(loginType.getRetryLimitExceed(), errorLimitTime); } else { 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 index 2f9d0020a..6525d31e9 100644 --- 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 @@ -68,7 +68,7 @@ public class SysMenuServiceImpl implements ISysMenuService { .orderByAsc(SysMenu::getOrderNum)); } else { QueryWrapper wrapper = Wrappers.query(); - wrapper.eq("ur.user_id", userId) + wrapper.eq("sur.user_id", userId) .like(StringUtils.isNotBlank(menu.getMenuName()), "m.menu_name", menu.getMenuName()) .eq(StringUtils.isNotBlank(menu.getVisible()), "m.visible", menu.getVisible()) .eq(StringUtils.isNotBlank(menu.getStatus()), "m.status", menu.getStatus()) diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssConfigServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssConfigServiceImpl.java index f78be1e83..015c156ab 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssConfigServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssConfigServiceImpl.java @@ -95,9 +95,10 @@ public class SysOssConfigServiceImpl implements ISysOssConfigService { SysOssConfig config = BeanUtil.toBean(bo, SysOssConfig.class); validEntityBeforeSave(config); LambdaUpdateWrapper luw = new LambdaUpdateWrapper<>(); - luw.set(StringUtils.isBlank(config.getPrefix()), SysOssConfig::getPrefix, ""); - luw.set(StringUtils.isBlank(config.getRegion()), SysOssConfig::getRegion, ""); - luw.set(StringUtils.isBlank(config.getExt1()), SysOssConfig::getExt1, ""); + luw.set(ObjectUtil.isNull(config.getPrefix()), SysOssConfig::getPrefix, ""); + luw.set(ObjectUtil.isNull(config.getRegion()), SysOssConfig::getRegion, ""); + luw.set(ObjectUtil.isNull(config.getExt1()), SysOssConfig::getExt1, ""); + luw.set(ObjectUtil.isNull(config.getRemark()), SysOssConfig::getRemark, ""); luw.eq(SysOssConfig::getOssConfigId, config.getOssConfigId()); return setConfigCache(baseMapper.update(config, luw) > 0, config); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssServiceImpl.java index 19423f404..03a6cfca8 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssServiceImpl.java @@ -1,5 +1,6 @@ package com.ruoyi.system.service.impl; +import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; @@ -7,9 +8,11 @@ import com.ruoyi.common.core.domain.PageQuery; import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.redis.RedisUtils; +import com.ruoyi.oss.constant.OssConstant; +import com.ruoyi.oss.core.OssClient; import com.ruoyi.oss.entity.UploadResult; import com.ruoyi.oss.factory.OssFactory; -import com.ruoyi.oss.service.IOssStrategy; import com.ruoyi.system.domain.SysOss; import com.ruoyi.system.domain.bo.SysOssBo; import com.ruoyi.system.domain.vo.SysOssVo; @@ -20,6 +23,8 @@ import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; +import java.time.Duration; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; @@ -42,6 +47,21 @@ public class SysOssServiceImpl implements ISysOssService { return TableDataInfo.build(result); } + @Override + public List listByIds(Collection ossIds) { + List list = new ArrayList<>(); + for (Long id : ossIds) { + String key = OssConstant.SYS_OSS_KEY + id; + SysOssVo vo = RedisUtils.getCacheObject(key); + if (ObjectUtil.isNull(vo)) { + vo = baseMapper.selectVoById(id); + RedisUtils.setCacheObject(key, vo, Duration.ofDays(30)); + } + list.add(vo); + } + return list; + } + private LambdaQueryWrapper buildQueryWrapper(SysOssBo bo) { Map params = bo.getParams(); LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); @@ -65,7 +85,7 @@ public class SysOssServiceImpl implements ISysOssService { public SysOss upload(MultipartFile file) { String originalfileName = file.getOriginalFilename(); String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length()); - IOssStrategy storage = OssFactory.instance(); + OssClient storage = OssFactory.instance(); UploadResult uploadResult; try { uploadResult = storage.uploadSuffix(file.getBytes(), suffix, file.getContentType()); @@ -78,7 +98,7 @@ public class SysOssServiceImpl implements ISysOssService { oss.setFileSuffix(suffix); oss.setFileName(uploadResult.getFilename()); oss.setOriginalName(originalfileName); - oss.setService(storage.getServiceType().getValue()); + oss.setService(storage.getConfigKey()); baseMapper.insert(oss); return oss; } @@ -90,7 +110,7 @@ public class SysOssServiceImpl implements ISysOssService { } List list = baseMapper.selectBatchIds(ids); for (SysOss sysOss : list) { - IOssStrategy storage = OssFactory.instance(sysOss.getService()); + OssClient storage = OssFactory.instance(sysOss.getService()); storage.delete(sysOss.getUrl()); } return baseMapper.deleteBatchIds(ids) > 0; diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysSensitiveServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysSensitiveServiceImpl.java index 08d4cc4f9..fe142ca3c 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysSensitiveServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysSensitiveServiceImpl.java @@ -20,7 +20,7 @@ public class SysSensitiveServiceImpl implements SensitiveService { */ @Override public boolean isSensitive() { - return LoginHelper.isAdmin(); + return !LoginHelper.isAdmin(); } } 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 index 2ef63881c..ed24ab4e9 100644 --- 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 @@ -1,6 +1,7 @@ package com.ruoyi.system.service.impl; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; @@ -115,11 +116,11 @@ public class SysUserServiceImpl implements ISysUserService { */ @Override public TableDataInfo selectUnallocatedList(SysUser user, PageQuery pageQuery) { - List userId = userRoleMapper.selectUserIdsByRoleId(user.getRoleId()); + List userIds = userRoleMapper.selectUserIdsByRoleId(user.getRoleId()); QueryWrapper wrapper = Wrappers.query(); wrapper.eq("u.del_flag", UserConstants.USER_NORMAL) .and(w -> w.ne("r.role_id", user.getRoleId()).or().isNull("r.role_id")) - .notIn("u.user_id", userId) + .notIn(CollUtil.isNotEmpty(userIds), "u.user_id", userIds) .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()) .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()); Page page = baseMapper.selectUnallocatedList(pageQuery.build(), wrapper); @@ -402,20 +403,7 @@ public class SysUserServiceImpl implements ISysUserService { * @param user 用户对象 */ public void insertUserRole(SysUser user) { - Long[] roles = user.getRoleIds(); - if (ObjectUtil.isNotNull(roles)) { - // 新增用户与角色管理 - List list = new ArrayList(); - for (Long roleId : roles) { - SysUserRole ur = new SysUserRole(); - ur.setUserId(user.getUserId()); - ur.setRoleId(roleId); - list.add(ur); - } - if (list.size() > 0) { - userRoleMapper.insertBatch(list); - } - } + this.insertUserRole(user.getUserId(), user.getRoleIds()); } /** @@ -425,18 +413,16 @@ public class SysUserServiceImpl implements ISysUserService { */ public void insertUserPost(SysUser user) { Long[] posts = user.getPostIds(); - if (ObjectUtil.isNotNull(posts)) { + if (ArrayUtil.isNotEmpty(posts)) { // 新增用户与岗位管理 - List list = new ArrayList(); + List list = new ArrayList<>(posts.length); for (Long postId : posts) { SysUserPost up = new SysUserPost(); up.setUserId(user.getUserId()); up.setPostId(postId); list.add(up); } - if (list.size() > 0) { - userPostMapper.insertBatch(list); - } + userPostMapper.insertBatch(list); } } @@ -447,18 +433,16 @@ public class SysUserServiceImpl implements ISysUserService { * @param roleIds 角色组 */ public void insertUserRole(Long userId, Long[] roleIds) { - if (ObjectUtil.isNotNull(roleIds)) { + if (ArrayUtil.isNotEmpty(roleIds)) { // 新增用户与角色管理 - List list = new ArrayList(); + List list = new ArrayList<>(roleIds.length); for (Long roleId : roleIds) { SysUserRole ur = new SysUserRole(); ur.setUserId(userId); ur.setRoleId(roleId); list.add(ur); } - if (list.size() > 0) { - userRoleMapper.insertBatch(list); - } + userRoleMapper.insertBatch(list); } } diff --git a/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml index 08838c599..2f69197b3 100644 --- a/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml @@ -32,8 +32,8 @@ m.perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time from sys_menu m left join sys_role_menu rm on m.menu_id = rm.menu_id - left join sys_user_role ur on rm.role_id = ur.role_id - left join sys_role ro on ur.role_id = ro.role_id + left join sys_user_role sur on rm.role_id = sur.role_id + left join sys_role ro on sur.role_id = ro.role_id ${ew.getCustomSqlSegment} @@ -55,9 +55,9 @@ m.create_time from sys_menu m left join sys_role_menu rm on m.menu_id = rm.menu_id - left join sys_user_role ur on rm.role_id = ur.role_id - left join sys_role ro on ur.role_id = ro.role_id - left join sys_user u on ur.user_id = u.user_id + left join sys_user_role sur on rm.role_id = sur.role_id + left join sys_role ro on sur.role_id = ro.role_id + left join sys_user u on sur.user_id = u.user_id where u.user_id = #{userId} and m.menu_type in ('M', 'C') and m.status = '0' @@ -81,18 +81,18 @@ select distinct m.perms from sys_menu m left join sys_role_menu rm on m.menu_id = rm.menu_id - left join sys_user_role ur on rm.role_id = ur.role_id + left join sys_user_role sur on rm.role_id = sur.role_id diff --git a/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml index dc54c7e9f..90dff5051 100644 --- a/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml @@ -34,8 +34,8 @@ r.create_time, r.remark from sys_role r - left join sys_user_role ur on ur.role_id = r.role_id - left join sys_user u on u.user_id = ur.user_id + left join sys_user_role sur on sur.role_id = r.role_id + left join sys_user u on u.user_id = sur.user_id left join sys_dept d on u.dept_id = d.dept_id @@ -51,14 +51,14 @@ diff --git a/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml index 0b388d136..dacdcd8a6 100644 --- a/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml @@ -80,12 +80,12 @@ 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 ur on u.user_id = ur.user_id - left join sys_role r on r.role_id = ur.role_id + left join sys_user_role sur on u.user_id = sur.user_id + left join sys_role r on r.role_id = sur.role_id @@ -113,8 +113,8 @@ select distinct u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.phonenumber, u.status, u.create_time from sys_user u left join sys_dept d on u.dept_id = d.dept_id - left join sys_user_role ur on u.user_id = ur.user_id - left join sys_role r on r.role_id = ur.role_id + left join sys_user_role sur on u.user_id = sur.user_id + left join sys_role r on r.role_id = sur.role_id ${ew.getCustomSqlSegment} diff --git a/ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml index 63a846d30..5a55fcb1e 100644 --- a/ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml @@ -10,8 +10,8 @@ diff --git a/ruoyi-ui/package.json b/ruoyi-ui/package.json index c7f0b7334..c9486724f 100644 --- a/ruoyi-ui/package.json +++ b/ruoyi-ui/package.json @@ -1,6 +1,6 @@ { "name": "ruoyi-vue-plus", - "version": "4.1.0", + "version": "4.2.0", "description": "RuoYi-Vue-Plus后台管理系统", "author": "LionLi", "license": "MIT", diff --git a/ruoyi-ui/src/api/login.js b/ruoyi-ui/src/api/login.js index 0327e6f9a..3c5bcfe21 100644 --- a/ruoyi-ui/src/api/login.js +++ b/ruoyi-ui/src/api/login.js @@ -57,3 +57,15 @@ export function getCodeImg() { timeout: 20000 }) } + +// 短信验证码 +export function getCodeSms() { + return request({ + url: '/captchaSms', + headers: { + isToken: false + }, + method: 'get', + timeout: 20000 + }) +} diff --git a/ruoyi-ui/src/api/system/oss.js b/ruoyi-ui/src/api/system/oss.js index 83adca54e..7d8002600 100644 --- a/ruoyi-ui/src/api/system/oss.js +++ b/ruoyi-ui/src/api/system/oss.js @@ -9,6 +9,14 @@ export function listOss(query) { }) } +// 查询OSS对象基于id串 +export function listByIds(ossId) { + return request({ + url: '/system/oss/listByIds/' + ossId, + method: 'get' + }) +} + // 删除OSS对象存储 export function delOss(ossId) { return request({ diff --git a/ruoyi-ui/src/assets/styles/element-variables.scss b/ruoyi-ui/src/assets/styles/element-variables.scss index 8b7a48e9e..1615ff289 100644 --- a/ruoyi-ui/src/assets/styles/element-variables.scss +++ b/ruoyi-ui/src/assets/styles/element-variables.scss @@ -17,7 +17,7 @@ $--button-font-weight: 400; $--border-color-light: #dfe4ed; $--border-color-lighter: #e6ebf5; -$--table-border:1px solid#dfe6ec; +$--table-border: 1px solid #dfe6ec; /* icon font path, required */ $--font-path: '~element-ui/lib/theme-chalk/fonts'; diff --git a/ruoyi-ui/src/components/FileUpload/index.vue b/ruoyi-ui/src/components/FileUpload/index.vue index 78baf48dd..e999f14f8 100644 --- a/ruoyi-ui/src/components/FileUpload/index.vue +++ b/ruoyi-ui/src/components/FileUpload/index.vue @@ -41,7 +41,7 @@ diff --git a/ruoyi-ui/src/components/ImagePreview/index.vue b/ruoyi-ui/src/components/ImagePreview/index.vue index 743d8d51d..671eda323 100644 --- a/ruoyi-ui/src/components/ImagePreview/index.vue +++ b/ruoyi-ui/src/components/ImagePreview/index.vue @@ -12,7 +12,6 @@ diff --git a/ruoyi-ui/src/main.js b/ruoyi-ui/src/main.js index 1e0144f40..caf30d747 100644 --- a/ruoyi-ui/src/main.js +++ b/ruoyi-ui/src/main.js @@ -73,6 +73,9 @@ DictData.install() * please remove it before going online! ! ! */ +// 修改 el-dialog 默认点击遮照为不关闭 +Element.Dialog.props.closeOnClickModal.default = false + Vue.use(Element, { size: Cookies.get('size') || 'medium' // set element-ui default size }) diff --git a/ruoyi-ui/src/utils/dict/DictMeta.js b/ruoyi-ui/src/utils/dict/DictMeta.js index 8ae6133ec..2ae08273a 100644 --- a/ruoyi-ui/src/utils/dict/DictMeta.js +++ b/ruoyi-ui/src/utils/dict/DictMeta.js @@ -11,7 +11,7 @@ import DictOptions from './DictOptions' export default class DictMeta { constructor(options) { this.type = options.type - this.request = options.request, + this.request = options.request this.responseConverter = options.responseConverter this.labelField = options.labelField this.valueField = options.valueField diff --git a/ruoyi-ui/src/utils/ruoyi.js b/ruoyi-ui/src/utils/ruoyi.js index 7e6eccd11..d2cd2a060 100644 --- a/ruoyi-ui/src/utils/ruoyi.js +++ b/ruoyi-ui/src/utils/ruoyi.js @@ -68,7 +68,7 @@ export function addDateRange(params, dateRange, propName) { return search; } -// 回显数据字典 +// 回显数据字典 export function selectDictLabel(datas, value) { if (value === undefined) { return ""; @@ -207,10 +207,10 @@ export function tansParams(params) { for (const propName of Object.keys(params)) { const value = params[propName]; var part = encodeURIComponent(propName) + "="; - if (value !== null && typeof (value) !== "undefined") { + if (value !== null && value !== "" && typeof (value) !== "undefined") { if (typeof value === 'object') { for (const key of Object.keys(value)) { - if (value[key] !== null && typeof (value[key]) !== 'undefined') { + if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') { let params = propName + '[' + key + ']'; var subPart = encodeURIComponent(params) + "="; result += subPart + encodeURIComponent(value[key]) + "&"; @@ -233,4 +233,4 @@ export async function blobValidate(data) { } catch (error) { return true; } -} \ No newline at end of file +} diff --git a/ruoyi-ui/src/views/index.vue b/ruoyi-ui/src/views/index.vue index 5a13cc4bd..1576cbd75 100644 --- a/ruoyi-ui/src/views/index.vue +++ b/ruoyi-ui/src/views/index.vue @@ -114,7 +114,7 @@ export default { data() { return { // 版本号 - version: "4.1.0", + version: "4.2.0", }; }, methods: { diff --git a/ruoyi-ui/src/views/system/dict/data.vue b/ruoyi-ui/src/views/system/dict/data.vue index f22099dd3..bdd095234 100644 --- a/ruoyi-ui/src/views/system/dict/data.vue +++ b/ruoyi-ui/src/views/system/dict/data.vue @@ -191,7 +191,7 @@