From e6d7a6db8eb713efbd9f16edb98871e193ef66a3 Mon Sep 17 00:00:00 2001
From: dulujia <17865799150@163.com>
Date: Wed, 3 Dec 2025 21:51:53 +0800
Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pom.xml | 9 ++
ruoyi-admin/pom.xml | 1 +
.../controller/system/SysUserController.java | 95 +++++++++++++++++--
.../src/main/resources/application-druid.yml | 4 +-
.../src/main/resources/application.yml | 15 ++-
ruoyi-common/pom.xml | 7 ++
.../com/ruoyi/common/utils/MailUtils.java | 59 ++++++++++++
ruoyi-ui/package.json | 1 +
ruoyi-ui/src/api/system/notice.js | 3 +-
ruoyi-ui/src/api/system/user.js | 11 +++
ruoyi-ui/src/views/login.vue | 2 +-
ruoyi-ui/src/views/system/user/index.vue | 90 +++++++++++++++++-
12 files changed, 283 insertions(+), 14 deletions(-)
create mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/utils/MailUtils.java
diff --git a/pom.xml b/pom.xml
index 66fc545cc..8236d1243 100644
--- a/pom.xml
+++ b/pom.xml
@@ -147,6 +147,15 @@
commons-io
${commons.io.version}
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-mail
+ ${spring-boot.version}
+
+
diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml
index 434b5f07d..62ae0f4ea 100644
--- a/ruoyi-admin/pom.xml
+++ b/ruoyi-admin/pom.xml
@@ -24,6 +24,7 @@
true
+
io.springfox
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java
index 11790f9ba..32887f1ce 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java
@@ -1,20 +1,15 @@
package com.ruoyi.web.controller.system;
import java.util.List;
+import java.util.Map;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
+import com.ruoyi.common.utils.MailUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.PutMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
@@ -53,6 +48,10 @@ public class SysUserController extends BaseController
@Autowired
private ISysPostService postService;
+ @Autowired
+ private MailUtils mailUtils;
+
+
/**
* 获取用户列表
*/
@@ -253,4 +252,84 @@ public class SysUserController extends BaseController
{
return success(deptService.selectDeptTreeList(dept));
}
+
+ /**
+ * 群发邮件
+ * type=1 按userIds type=2 按条件 type=3 按部门 type=4 按角色
+ */
+ @PreAuthorize("@ss.hasPermi('system:user:mail')")
+ @Log(title = "用户管理", businessType = BusinessType.OTHER)
+ @PostMapping("/mail/batch")
+ public AjaxResult sendMailBatch(@RequestBody Map requestData) {
+ // 从请求体中提取所有参数
+ String type = (String) requestData.get("type");
+ String subject = (String) requestData.get("subject");
+ String html = (String) requestData.get("html");
+
+ // 验证必需参数
+ if (StringUtils.isEmpty(type) || StringUtils.isEmpty(subject) || StringUtils.isEmpty(html)) {
+ return error("缺少必要参数");
+ }
+
+ // 提取可选参数
+ List userIds = null;
+ Long deptId = null;
+ SysUser queryUser = null;
+
+ if (requestData.containsKey("userIds")) {
+ userIds = (List) requestData.get("userIds");
+ }
+ if (requestData.containsKey("deptId")) {
+ Object deptIdObj = requestData.get("deptId");
+ if (deptIdObj instanceof Number) {
+ deptId = ((Number) deptIdObj).longValue();
+ } else {
+ deptId = Long.valueOf(deptIdObj.toString());
+ }
+ }
+ if (requestData.containsKey("queryUser")) {
+ Map userMap = (Map) requestData.get("queryUser");
+ queryUser = new SysUser();
+ // 根据需要设置queryUser的属性
+ if (userMap.containsKey("userName")) {
+ queryUser.setUserName((String) userMap.get("userName"));
+ }
+ if (userMap.containsKey("phonenumber")) {
+ queryUser.setPhonenumber((String) userMap.get("phonenumber"));
+ }
+ if (userMap.containsKey("status")) {
+ queryUser.setStatus((String) userMap.get("status"));
+ }
+ }
+
+ SysUser cond = new SysUser();
+ if ("1".equals(type) && userIds != null && !userIds.isEmpty()) {
+ cond.getParams().put("userIds", userIds);
+ } else if ("2".equals(type) && queryUser != null) {
+ cond = queryUser;
+ } else if ("3".equals(type) && deptId != null) {
+ cond.setDeptId(deptId);
+ } else if ("4".equals(type) && deptId != null) {
+ cond.getParams().put("roleId", deptId);
+ }
+
+ List users = userService.selectUserList(cond);
+ if (users.isEmpty()) {
+ return error("没有符合条件的用户");
+ }
+
+ List mails = users.stream()
+ .filter(u -> StringUtils.isNotBlank(u.getEmail()))
+ .map(SysUser::getEmail)
+ .distinct()
+ .collect(Collectors.toList());
+
+ if (mails.isEmpty()) {
+ return error("这些用户都没有维护邮箱");
+ }
+
+ mailUtils.sendHtmlBatch(mails, subject, html);
+ return success("已发送 " + mails.size() + " 封邮件");
+ }
+
}
diff --git a/ruoyi-admin/src/main/resources/application-druid.yml b/ruoyi-admin/src/main/resources/application-druid.yml
index bcfad3eae..81948b839 100644
--- a/ruoyi-admin/src/main/resources/application-druid.yml
+++ b/ruoyi-admin/src/main/resources/application-druid.yml
@@ -6,9 +6,9 @@ spring:
druid:
# 主库数据源
master:
- url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+ url: jdbc:mysql://localhost:3306/ry1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
- password: password
+ password: 123456
# 从库数据源
slave:
# 从数据源开关/默认关闭
diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml
index d15705c48..793a3f9e5 100644
--- a/ruoyi-admin/src/main/resources/application.yml
+++ b/ruoyi-admin/src/main/resources/application.yml
@@ -47,6 +47,17 @@ user:
# Spring配置
spring:
+ mail:
+ host: smtp.163.com
+ port: 465
+ username: 17865799150@163.com # ← 你的邮箱
+ password: XGgv736G8saB6a23 # ← 下面步骤获取的「授权码」
+ protocol: smtps
+ properties:
+ mail.smtp.ssl.enable: true
+ mail.smtp.auth: true
+ mail.smtp.timeout: 5000
+ mail.smtp.connectiontimeout: 5000
# 资源信息
messages:
# 国际化资源文件路径
@@ -72,7 +83,7 @@ spring:
# 端口,默认为6379
port: 6379
# 数据库索引
- database: 0
+ database: 10
# 密码
password:
# 连接超时时间
@@ -88,6 +99,7 @@ spring:
# #连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
+
# token配置
token:
# 令牌自定义标识
@@ -134,3 +146,4 @@ xss:
excludes: /system/notice
# 匹配链接
urlPatterns: /system/*,/monitor/*,/tool/*
+
diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml
index 6e6b98808..ef21c3f7e 100644
--- a/ruoyi-common/pom.xml
+++ b/ruoyi-common/pom.xml
@@ -35,6 +35,13 @@
spring-boot-starter-security
+
+
+ org.springframework.boot
+ spring-boot-starter-mail
+ ${spring-boot.version}
+
+
com.github.pagehelper
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/MailUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/MailUtils.java
new file mode 100644
index 000000000..cf7e82691
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/MailUtils.java
@@ -0,0 +1,59 @@
+package com.ruoyi.common.utils;
+
+import java.io.File;
+import java.util.List;
+import javax.mail.internet.MimeMessage;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.mail.javamail.MimeMessageHelper;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+
+@Component
+public class MailUtils {
+
+ @Resource
+ private JavaMailSender mailSender;
+
+ @Value("${spring.mail.username}")
+ private String from;
+
+ /** 纯文本 */
+ public void sendText(String to, String subject, String content) {
+ send(to, subject, content, false, null);
+ }
+
+ /** HTML 单发 */
+ public void sendHtml(String to, String subject, String html) {
+ send(to, subject, html, true, null);
+ }
+
+ /** HTML 群发(抄送模式,性能足够) */
+ public void sendHtmlBatch(List toList, String subject, String html) {
+ if (toList == null || toList.isEmpty()) return;
+ String[] toArray = toList.toArray(new String[0]);
+ send(String.join(",", toArray), subject, html, true, null);
+ }
+
+ /** 带附件 */
+ public void sendWithFile(String to, String subject, String html, File file) {
+ send(to, subject, html, true, file);
+ }
+
+ /** 真正发送 */
+ private void send(String to, String subject, String text, boolean html, File file) {
+ try {
+ MimeMessage message = mailSender.createMimeMessage();
+ MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
+ helper.setFrom(from);
+ helper.setTo(to.split(","));
+ helper.setSubject(subject);
+ helper.setText(text, html);
+ if (file != null) helper.addAttachment(file.getName(), file);
+ mailSender.send(message);
+ } catch (Exception e) {
+ throw new RuntimeException("邮件发送失败", e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-ui/package.json b/ruoyi-ui/package.json
index 5df0cedfc..35fa4a2b2 100644
--- a/ruoyi-ui/package.json
+++ b/ruoyi-ui/package.json
@@ -49,6 +49,7 @@
"vuex": "3.6.0"
},
"devDependencies": {
+ "@types/qs": "^6.14.0",
"@vue/cli-plugin-babel": "4.4.6",
"@vue/cli-service": "4.4.6",
"babel-plugin-dynamic-import-node": "2.3.3",
diff --git a/ruoyi-ui/src/api/system/notice.js b/ruoyi-ui/src/api/system/notice.js
index 737fc169d..c50186d4c 100644
--- a/ruoyi-ui/src/api/system/notice.js
+++ b/ruoyi-ui/src/api/system/notice.js
@@ -41,4 +41,5 @@ export function delNotice(noticeId) {
url: '/system/notice/' + noticeId,
method: 'delete'
})
-}
\ No newline at end of file
+
+}
diff --git a/ruoyi-ui/src/api/system/user.js b/ruoyi-ui/src/api/system/user.js
index 2da13d483..5b8c8b893 100644
--- a/ruoyi-ui/src/api/system/user.js
+++ b/ruoyi-ui/src/api/system/user.js
@@ -1,3 +1,4 @@
+import qs from 'qs'
import request from '@/utils/request'
import { parseStrEmpty } from "@/utils/ruoyi";
@@ -10,6 +11,16 @@ export function listUser(query) {
})
}
+export function sendMailBatch(data) {
+ return request({
+ url: '/system/user/mail/batch',
+ method: 'post',
+ data: data // 所有参数都在请求体中
+ })
+}
+
+
+
// 查询用户详细
export function getUser(userId) {
return request({
diff --git a/ruoyi-ui/src/views/login.vue b/ruoyi-ui/src/views/login.vue
index f979f68b5..e0fbee4c5 100644
--- a/ruoyi-ui/src/views/login.vue
+++ b/ruoyi-ui/src/views/login.vue
@@ -56,7 +56,7 @@
diff --git a/ruoyi-ui/src/views/system/user/index.vue b/ruoyi-ui/src/views/system/user/index.vue
index 9d6e61c53..838fb3532 100644
--- a/ruoyi-ui/src/views/system/user/index.vue
+++ b/ruoyi-ui/src/views/system/user/index.vue
@@ -50,6 +50,12 @@
导入
+
+
+ 群发邮件
+
+
导出
@@ -63,6 +69,7 @@
+
@@ -178,6 +185,24 @@
取 消
+
+
+
+
+
+
+
+
+
+
+ 支持 {userName} {deptName} 会自动替换
+
+
+
+
@@ -198,10 +223,12 @@
+
+