diff --git a/pom.xml b/pom.xml index 4fc098f5b..71963f204 100644 --- a/pom.xml +++ b/pom.xml @@ -38,6 +38,7 @@ 3.5.1 1.3.6 2.3.0 + 1.6.2 3.0.1 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..2b9917f7c 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 @@ -19,9 +19,9 @@ import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; +import java.time.Duration; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.TimeUnit; /** * 验证码操作处理 @@ -60,7 +60,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/resources/application-dev.yml b/ruoyi-admin/src/main/resources/application-dev.yml index 45eb5be97..50e060b62 100644 --- a/ruoyi-admin/src/main/resources/application-dev.yml +++ b/ruoyi-admin/src/main/resources/application-dev.yml @@ -137,6 +137,34 @@ spring: # 是否开启ssl ssl: false + # 邮件 + mail: + enabled: false + # 邮件服务地址 (enabled = true 时打开该配置) +# host: smtp.qq.com + # 用户名 + username: xxx@qq.com + # 授权码 (设置 - 账户 - POP3/SMTP服务) + password: xxx + # QQ邮箱加密端口,不同邮箱的端口不一样 + port: 465 + properties: + mail: + smtp: + socketFactory: + class: javax.net.ssl.SSLSocketFactory + ssl: + trust: smtp.qq.com + # 是否需要用户认证 + auth: true + starttls: + # 启用TLS加密 + enable: true + required: true + # 传输协议 starttls.enable = true 时为 smtps + protocol: smtps + debug: true + redisson: # 线程池数量 threads: 4 diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml index bb476cbdd..fd6a999fe 100644 --- a/ruoyi-common/pom.xml +++ b/ruoyi-common/pom.xml @@ -159,6 +159,12 @@ lock4j-redisson-spring-boot-starter + + com.sun.mail + javax.mail + ${mail.version} + + 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..f8688c7b2 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/email/MailUtils.java @@ -0,0 +1,471 @@ +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 org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.JavaMailSenderImpl; + +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; +import java.util.Properties; + + +/** + * 邮件工具类 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class MailUtils extends MailUtil { + + private static final MailAccount ACCOUNT = SpringUtils.getBean(MailAccount.class); + + /** + * 获取邮件发送实例 + */ + public static MailAccount getMailAccount() { + return ACCOUNT; + } + + /** + * 获取邮件发送实例 (自定义发送人以及授权码) + * + * @param username 发送人 + * @param password 授权码 + */ + public static MailAccount getMailAccount(String username, String password) { + ACCOUNT.setFrom(StringUtils.blankToDefault(username, ACCOUNT.getUser())); + ACCOUNT.setUser(StringUtils.blankToDefault(username, ACCOUNT.getUser())); + ACCOUNT.setPass(StringUtils.blankToDefault(password, 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/poi/ExcelUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java index 36dc67fa1..78d5915ee 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 @@ -103,7 +103,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 +132,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..b0f35d878 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); } /** 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..8051902bf --- /dev/null +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/MailController.java @@ -0,0 +1,56 @@ +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 lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +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; + + +/** + * 测试邮件发送 Controller + * + * @author Michelle.Chung + */ +@Validated +@Api(value = "邮件控制器", tags = {"测试邮件发送"}) +@RequiredArgsConstructor(onConstructor_ = @Autowired) +@RequestMapping("/demo/mail") +@RestController +public class MailController { + + /** + * 发送邮件 + * + * @param to 接收人 + * @param subject 标题 + * @param text 内容 + */ + @GetMapping("/sendSimpleMessage") + public R sendSimpleMessage(String to, String subject, String text) { + MailUtils.send(to, subject, text, false); + + return R.ok("操作成功"); + } + + /** + * 发送邮件(带附件) + * + * @param to 接收人 + * @param subject 标题 + * @param text 内容 + */ + @GetMapping("/sendMessageWithAttachment") + public R sendMessageWithAttachment(String to, String subject, String text, String filePath) { + MailUtils.send(to, subject, text, false, 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-framework/src/main/java/com/ruoyi/framework/aspectj/RepeatSubmitAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RepeatSubmitAspect.java index aedc4431c..b77acb40e 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RepeatSubmitAspect.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RepeatSubmitAspect.java @@ -25,9 +25,9 @@ import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.time.Duration; import java.util.Collection; import java.util.Map; -import java.util.concurrent.TimeUnit; /** * 防止重复提交(参考美团GTIS防重系统) @@ -66,7 +66,7 @@ public class RepeatSubmitAspect { String cacheRepeatKey = Constants.REPEAT_SUBMIT_KEY + url + submitKey; String key = RedisUtils.getCacheObject(cacheRepeatKey); if (key == null) { - RedisUtils.setCacheObject(cacheRepeatKey, "", interval, TimeUnit.MILLISECONDS); + RedisUtils.setCacheObject(cacheRepeatKey, "", Duration.ofMillis(interval)); KEY_CACHE.set(cacheRepeatKey); } else { String message = repeatSubmit.message(); diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MailConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MailConfig.java new file mode 100644 index 000000000..13eeeec97 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MailConfig.java @@ -0,0 +1,40 @@ +package com.ruoyi.framework.config; + +import cn.hutool.extra.mail.MailAccount; +import com.ruoyi.framework.config.properties.MailProperties; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.Resource; + +/** + * JavaMail 配置 + * + * @author Michelle.Chung + */ +@Configuration +public class MailConfig { + + @Resource + private MailProperties mailProperties; + + /** + * 初始化 MailAccount + */ + @Bean + @ConditionalOnProperty(value = "spring.mail.enabled", havingValue = "true") + public MailAccount mailAccount() { + MailAccount account = new MailAccount(); + account.setUser(mailProperties.getUsername()); + account.setFrom(mailProperties.getUsername()); + account.setPass(mailProperties.getPassword()); + account.setHost(mailProperties.getHost()); + account.setPort(mailProperties.getPort()); + account.setAuth(mailProperties.getAuth()); + account.setDebug(mailProperties.getDebug()); + account.setStarttlsEnable(mailProperties.getStarttlsEnable()); + return account; + } + +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java index da18cfd17..d8cbba25d 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java @@ -1,6 +1,7 @@ package com.ruoyi.framework.config; import cn.hutool.core.util.ObjectUtil; +import com.ruoyi.common.utils.JsonUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.framework.config.properties.RedissonProperties; import lombok.extern.slf4j.Slf4j; @@ -53,7 +54,7 @@ public class RedisConfig extends CachingConfigurerSupport { Config config = new Config(); config.setThreads(redissonProperties.getThreads()) .setNettyThreads(redissonProperties.getNettyThreads()) - .setCodec(JsonJacksonCodec.INSTANCE); + .setCodec(new JsonJacksonCodec(JsonUtils.getObjectMapper())); RedissonProperties.SingleServerConfig singleServerConfig = redissonProperties.getSingleServerConfig(); if (ObjectUtil.isNotNull(singleServerConfig)) { diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java index 22ef7c1b5..2424981c1 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java @@ -109,7 +109,7 @@ public class SwaggerConfig { * 安全模式,这里指定token通过Authorization头请求头传递 */ private List securitySchemes() { - List apiKeyList = new ArrayList(); + List apiKeyList = new ArrayList<>(); String header = saTokenConfig.getTokenName(); apiKeyList.add(new ApiKey(header, header, In.HEADER.toValue())); return apiKeyList; diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java index ebf236c87..a85ad1e57 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java @@ -33,8 +33,8 @@ public class ThreadPoolConfig { @ConditionalOnProperty(prefix = "thread-pool", name = "enabled", havingValue = "true") public ThreadPoolTaskExecutor threadPoolTaskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); - executor.setMaxPoolSize(core); - executor.setCorePoolSize(core * 2); + executor.setCorePoolSize(core); + executor.setMaxPoolSize(core * 2); executor.setQueueCapacity(threadPoolProperties.getQueueCapacity()); executor.setKeepAliveSeconds(threadPoolProperties.getKeepAliveSeconds()); executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/MailProperties.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/MailProperties.java new file mode 100644 index 000000000..ce2103fea --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/MailProperties.java @@ -0,0 +1,65 @@ +package com.ruoyi.framework.config.properties; + +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * JavaMail 配置属性 + * + * @author Michelle.Chung + */ +@Data +@Component +@ConfigurationProperties(prefix = "spring.mail") +public class MailProperties { + + /** + * 过滤开关 + */ + private String enabled; + + /** + * 邮件服务地址 + */ + private String host; + + /** + * 用户名 + */ + private String username; + + /** + * 授权码 (设置 - 账户 - POP3/SMTP服务) + */ + private String password; + + /** + * 邮箱加密端口,不同邮箱的端口不一样 + */ + private Integer port; + + /** + * 是否需要用户认证 + */ + @Value("${spring.mail.properties.mail.smtp.auth}") + private Boolean auth; + + /** + * 是否启用TLS加密 + */ + @Value("${spring.mail.properties.mail.smtp.starttls.enable}") + private Boolean starttlsEnable; + + @Value("${spring.mail.properties.mail.smtp.ssl.trust}") + private String sslTrust; + + private Boolean debug; + + /** + * 传输协议 starttls.enable = true 时为 smtps + */ + private String protocol; + +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/listener/UserActionListener.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/listener/UserActionListener.java index 75277c257..88b23648e 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/listener/UserActionListener.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/listener/UserActionListener.java @@ -18,7 +18,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; -import java.util.concurrent.TimeUnit; +import java.time.Duration; /** * 用户行为 侦听器的实现 @@ -52,7 +52,7 @@ public class UserActionListener implements SaTokenListener { dto.setTokenId(tokenValue); dto.setUserName(user.getUsername()); dto.setDeptName(user.getDeptName()); - RedisUtils.setCacheObject(Constants.ONLINE_TOKEN_KEY + tokenValue, dto, tokenConfig.getTimeout(), TimeUnit.SECONDS); + RedisUtils.setCacheObject(Constants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(tokenConfig.getTimeout())); log.info("user doLogin, userId:{}, token:{}", loginId, tokenValue); } else if (userType == UserType.APP_USER) { // app端 自行根据业务编写 diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/satoken/dao/PlusSaTokenDao.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/satoken/dao/PlusSaTokenDao.java index f78e81415..a08503dc1 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/satoken/dao/PlusSaTokenDao.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/satoken/dao/PlusSaTokenDao.java @@ -5,10 +5,10 @@ import cn.dev33.satoken.util.SaFoxUtil; import com.ruoyi.common.utils.redis.RedisUtils; import org.springframework.stereotype.Component; +import java.time.Duration; import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.concurrent.TimeUnit; /** * Sa-Token持久层接口(使用框架自带RedisUtils实现 协议统一) @@ -38,7 +38,7 @@ public class PlusSaTokenDao implements SaTokenDao { if (timeout == SaTokenDao.NEVER_EXPIRE) { RedisUtils.setCacheObject(key, value); } else { - RedisUtils.setCacheObject(key, value, timeout, TimeUnit.SECONDS); + RedisUtils.setCacheObject(key, value, Duration.ofSeconds(timeout)); } } @@ -87,7 +87,7 @@ public class PlusSaTokenDao implements SaTokenDao { } return; } - RedisUtils.expire(key, timeout, TimeUnit.SECONDS); + RedisUtils.expire(key, Duration.ofSeconds(timeout)); } @@ -111,7 +111,7 @@ public class PlusSaTokenDao implements SaTokenDao { if (timeout == SaTokenDao.NEVER_EXPIRE) { RedisUtils.setCacheObject(key, object); } else { - RedisUtils.setCacheObject(key, object, timeout, TimeUnit.SECONDS); + RedisUtils.setCacheObject(key, object, Duration.ofSeconds(timeout)); } } @@ -160,7 +160,7 @@ public class PlusSaTokenDao implements SaTokenDao { } return; } - RedisUtils.expire(key, timeout, TimeUnit.SECONDS); + RedisUtils.expire(key, Duration.ofSeconds(timeout)); } 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..f60419f05 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; /** @@ -248,7 +248,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/SysUserServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java index 2ef63881c..ec919f63a 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 @@ -115,11 +115,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);