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 @@ + +