sms、邮件数据库配置

This commit is contained in:
2025-11-26 13:38:36 +08:00
parent 8d8ecf8763
commit 4ff1bc1101
16 changed files with 847 additions and 186 deletions

View File

@@ -19,4 +19,22 @@ INSERT INTO `tb_sys_config` (`id`, `config_key`, `config_name`, `config_value`,
-- Dify知识库配置
('30', 'dify.dataset.defaultIndexingTechnique', '默认索引方式', 'high_quality', 'string', 'select', 'Dify配置', '默认索引方式', NULL, '知识库文档的默认索引方式', NULL, NULL, NULL, NULL, 'high_quality,economy', 30, 1, '1', now()),
('31', 'dify.dataset.defaultEmbeddingModel', '默认Embedding模型', 'text-embedding-ada-002', 'string', 'input', 'Dify配置', '默认Embedding模型', '请输入Embedding模型名称', '知识库使用的默认Embedding模型', NULL, NULL, NULL, NULL, NULL, 31, 1, '1', now());
('31', 'dify.dataset.defaultEmbeddingModel', '默认Embedding模型', 'text-embedding-ada-002', 'string', 'input', 'Dify配置', '默认Embedding模型', '请输入Embedding模型名称', '知识库使用的默认Embedding模型', NULL, NULL, NULL, NULL, NULL, 31, 1, '1', now()),
-- 邮件配置
('40', 'email.host', 'SMTP服务器地址', 'smtp.qq.com', 'string', 'input', '邮件配置', 'SMTP服务器地址', '请输入SMTP服务器地址', '邮件发送服务器地址', NULL, NULL, NULL, NULL, NULL, 40, 1, '1', now()),
('41', 'email.port', 'SMTP端口', '587', 'integer', 'input', '邮件配置', 'SMTP服务器端口', '请输入SMTP端口', '邮件发送服务器端口', NULL, 25, 587, NULL, NULL, 41, 1, '1', now()),
('42', 'email.username', '发件人邮箱', '3223905473@qq.com', 'string', 'input', '邮件配置', '发件人邮箱地址', '请输入发件人邮箱', '用于发送邮件的邮箱账号', NULL, NULL, NULL, NULL, NULL, 42, 1, '1', now()),
('43', 'email.password', '邮箱授权码', 'xmdmxvtjumxocicc', 'string', 'password', '邮件配置', '邮箱授权码/密码', '请输入邮箱授权码', '邮箱的授权码或密码', NULL, NULL, NULL, NULL, NULL, 43, 1, '1', now()),
('44', 'email.fromName', '发件人名称', '校园新闻系统', 'string', 'input', '邮件配置', '发件人显示名称', '请输入发件人名称', '邮件中显示的发件人名称', NULL, NULL, NULL, NULL, NULL, 44, 1, '1', now()),
('45', 'email.ssl.enable', '启用SSL', 'true', 'boolean', 'switch', '邮件配置', '是否启用SSL', NULL, 'SSL加密连接', NULL, NULL, NULL, NULL, NULL, 45, 1, '1', now()),
('46', 'email.timeout', '连接超时时间', '30000', 'integer', 'input', '邮件配置', '连接超时时间(毫秒)', '请输入超时时间', 'SMTP连接超时时间', NULL, 5000, 60000, '毫秒', NULL, 46, 1, '1', now()),
-- 短信配置
('50', 'sms.provider', '短信服务商', 'aliyun', 'string', 'select', '短信配置', '短信服务提供商', NULL, '短信服务提供商类型', NULL, NULL, NULL, NULL, 'aliyun,tencent', 50, 1, '1', now()),
('51', 'sms.accessKeyId', 'AccessKey ID', 'LTAI5t68do3qVXx5Rufugt3X', 'string', 'input', '短信配置', '短信服务AccessKey ID', '请输入AccessKey ID', '云服务商的AccessKey ID', NULL, NULL, NULL, NULL, NULL, 51, 1, '1', now()),
('52', 'sms.accessKeySecret', 'AccessKey Secret', '2vD9ToIff49Vph4JQXsn0Cy8nXQfzA', 'string', 'password', '短信配置', '短信服务AccessKey Secret', '请输入AccessKey Secret', '云服务商的AccessKey Secret', NULL, NULL, NULL, NULL, NULL, 52, 1, '1', now()),
('53', 'sms.signName', '短信签名', '星洋智慧', 'string', 'input', '短信配置', '短信签名', '请输入短信签名', '发送短信使用的签名', NULL, NULL, NULL, NULL, NULL, 53, 1, '1', now()),
('54', 'sms.templateCode.login', '登录验证码模板', 'SMS_491985030', 'string', 'input', '短信配置', '登录验证码模板编码', '请输入模板编码', '登录验证码短信模板', NULL, NULL, NULL, NULL, NULL, 54, 1, '1', now()),
('55', 'sms.templateCode.register', '注册验证码模板', 'SMS_491985030', 'string', 'input', '短信配置', '注册验证码模板编码', '请输入模板编码', '注册验证码短信模板', NULL, NULL, NULL, NULL, NULL, 55, 1, '1', now()),
('56', 'sms.timeout', '请求超时时间', '30000', 'integer', 'input', '短信配置', '请求超时时间(毫秒)', '请输入超时时间', 'API请求超时时间', NULL, 5000, 60000, '毫秒', NULL, 56, 1, '1', now());

View File

@@ -178,30 +178,4 @@ public interface MessageService {
// ================== 辅助接口 ==================
/**
* 获取可选的部门树(当前部门及子部门)
*
* @return ResultDomain<Map> 部门树数据
* @author Claude
* @since 2025-11-13
*/
ResultDomain<Map<String, Object>> getTargetDepts();
/**
* 获取可选的角色列表(当前部门及子部门的角色)
*
* @return ResultDomain<Map> 角色列表数据
* @author Claude
* @since 2025-11-13
*/
ResultDomain<Map<String, Object>> getTargetRoles();
/**
* 获取可选的用户列表(当前部门及子部门的用户)
*
* @return ResultDomain<Map> 用户列表数据
* @author Claude
* @since 2025-11-13
*/
ResultDomain<Map<String, Object>> getTargetUsers();
}

View File

@@ -0,0 +1,91 @@
package org.xyzh.common.config.properties;
import org.springframework.stereotype.Component;
/**
* @description 邮件配置属性
* @filename EmailConfigProperties.java
* @author yslg
* @copyright xyzh
* @since 2025-11-26
*/
@Component
public class EmailConfigProperties {
/** SMTP服务器地址 */
private String host;
/** SMTP端口 */
private Integer port;
/** 发件人邮箱 */
private String username;
/** 邮箱授权码 */
private String password;
/** 发件人名称 */
private String fromName;
/** 是否启用SSL */
private Boolean sslEnable;
/** 连接超时时间(毫秒) */
private Integer timeout;
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public Integer getPort() {
return port;
}
public void setPort(Integer port) {
this.port = port;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getFromName() {
return fromName;
}
public void setFromName(String fromName) {
this.fromName = fromName;
}
public Boolean getSslEnable() {
return sslEnable;
}
public void setSslEnable(Boolean sslEnable) {
this.sslEnable = sslEnable;
}
public Integer getTimeout() {
return timeout;
}
public void setTimeout(Integer timeout) {
this.timeout = timeout;
}
}

View File

@@ -0,0 +1,91 @@
package org.xyzh.common.config.properties;
import org.springframework.stereotype.Component;
/**
* @description 短信配置属性
* @filename SmsConfigProperties.java
* @author yslg
* @copyright xyzh
* @since 2025-11-26
*/
@Component
public class SmsConfigProperties {
/** 短信服务商 */
private String provider;
/** AccessKey ID */
private String accessKeyId;
/** AccessKey Secret */
private String accessKeySecret;
/** 短信签名 */
private String signName;
/** 登录验证码模板 */
private String templateCodeLogin;
/** 注册验证码模板 */
private String templateCodeRegister;
/** 请求超时时间(毫秒) */
private Integer timeout;
public String getProvider() {
return provider;
}
public void setProvider(String provider) {
this.provider = provider;
}
public String getAccessKeyId() {
return accessKeyId;
}
public void setAccessKeyId(String accessKeyId) {
this.accessKeyId = accessKeyId;
}
public String getAccessKeySecret() {
return accessKeySecret;
}
public void setAccessKeySecret(String accessKeySecret) {
this.accessKeySecret = accessKeySecret;
}
public String getSignName() {
return signName;
}
public void setSignName(String signName) {
this.signName = signName;
}
public String getTemplateCodeLogin() {
return templateCodeLogin;
}
public void setTemplateCodeLogin(String templateCodeLogin) {
this.templateCodeLogin = templateCodeLogin;
}
public String getTemplateCodeRegister() {
return templateCodeRegister;
}
public void setTemplateCodeRegister(String templateCodeRegister) {
this.templateCodeRegister = templateCodeRegister;
}
public Integer getTimeout() {
return timeout;
}
public void setTimeout(Integer timeout) {
this.timeout = timeout;
}
}

View File

@@ -19,6 +19,13 @@
</properties>
<dependencies>
<!-- Common Core -->
<dependency>
<groupId>org.xyzh</groupId>
<artifactId>common-core</artifactId>
<version>1.0.0</version>
</dependency>
<!-- Apache POI for Excel -->
<dependency>
<groupId>org.apache.poi</groupId>

View File

@@ -3,11 +3,11 @@ package org.xyzh.common.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import org.xyzh.common.config.properties.EmailConfigProperties;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
@@ -27,8 +27,8 @@ public class EmailUtils {
@Autowired
private JavaMailSender mailSender;
@Value("${spring.mail.username:}")
private String from;
@Autowired
private EmailConfigProperties emailConfigProperties;
/**
* 发送简单文本邮件
@@ -40,6 +40,7 @@ public class EmailUtils {
public boolean sendSimpleEmail(String to, String subject, String content) {
try {
SimpleMailMessage message = new SimpleMailMessage();
String from = emailConfigProperties.getUsername();
message.setFrom(from);
message.setTo(to);
message.setSubject(subject);
@@ -66,6 +67,7 @@ public class EmailUtils {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
String from = emailConfigProperties.getUsername();
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(subject);

View File

@@ -6,9 +6,10 @@ import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
import com.aliyun.teaopenapi.models.Config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.xyzh.common.config.properties.SmsConfigProperties;
/**
* @description 短信发送工具类 - 支持多种短信服务商
@@ -22,29 +23,8 @@ public class SmsUtils {
private static final Logger logger = LoggerFactory.getLogger(SmsUtils.class);
@Value("${sms.enabled:false}")
private boolean enabled;
@Value("${sms.provider:aliyun}")
private String provider;
@Value("${sms.access-key-id:}")
private String accessKeyId;
@Value("${sms.access-key-secret:}")
private String accessKeySecret;
@Value("${sms.sign-name:红色思政学习平台}")
private String signName;
@Value("${sms.template-code:}")
private String templateCode;
@Value("${sms.region-id:cn-hangzhou}")
private String regionId;
@Value("${sms.endpoint:dysmsapi.aliyuncs.com}")
private String endpoint;
@Autowired
private SmsConfigProperties smsConfigProperties;
/**
* 发送短信验证码
@@ -54,16 +34,22 @@ public class SmsUtils {
*/
public boolean sendVerificationCode(String phone, String code) {
// 如果未启用短信服务,使用模拟模式
if (!enabled || !StringUtils.hasText(accessKeyId) || !StringUtils.hasText(accessKeySecret)) {
String accessKeyId = smsConfigProperties.getAccessKeyId();
String accessKeySecret = smsConfigProperties.getAccessKeySecret();
if (!StringUtils.hasText(accessKeyId) || !StringUtils.hasText(accessKeySecret)) {
logger.warn("短信服务未配置或未启用,使用模拟模式");
logger.info("【模拟发送】短信验证码,手机号: {}, 验证码: {}", phone, code);
return true;
}
// 根据配置的服务商选择发送方式
String provider = smsConfigProperties.getProvider();
if (provider == null) provider = "aliyun";
switch (provider.toLowerCase()) {
case "aliyun":
return sendByAliyun(phone, code);
return sendByAliyun(phone, code, smsConfigProperties.getTemplateCodeLogin());
case "tencent":
logger.warn("腾讯云短信服务暂未实现,使用模拟模式");
logger.info("【模拟发送】短信验证码,手机号: {}, 验证码: {}", phone, code);
@@ -78,15 +64,16 @@ public class SmsUtils {
* 使用阿里云发送短信验证码
* @param phone 手机号
* @param code 验证码
* @param templateCode 短信模板CODE
* @return 是否发送成功
*/
private boolean sendByAliyun(String phone, String code) {
private boolean sendByAliyun(String phone, String code, String templateCode) {
try {
Client client = createAliyunClient();
SendSmsRequest request = new SendSmsRequest()
.setPhoneNumbers(phone)
.setSignName(signName)
.setSignName(smsConfigProperties.getSignName())
.setTemplateCode(templateCode)
.setTemplateParam("{\"code\":\"" + code + "\"}");
@@ -112,9 +99,9 @@ public class SmsUtils {
*/
private Client createAliyunClient() throws Exception {
Config config = new Config()
.setAccessKeyId(accessKeyId)
.setAccessKeySecret(accessKeySecret)
.setEndpoint(endpoint);
.setAccessKeyId(smsConfigProperties.getAccessKeyId())
.setAccessKeySecret(smsConfigProperties.getAccessKeySecret())
.setEndpoint("dysmsapi.aliyuncs.com");
return new Client(config);
}
@@ -127,13 +114,19 @@ public class SmsUtils {
*/
public boolean sendSms(String phone, String templateCode, String templateParam) {
// 如果未启用短信服务,使用模拟模式
if (!enabled || !StringUtils.hasText(accessKeyId) || !StringUtils.hasText(accessKeySecret)) {
String accessKeyId = smsConfigProperties.getAccessKeyId();
String accessKeySecret = smsConfigProperties.getAccessKeySecret();
if (!StringUtils.hasText(accessKeyId) || !StringUtils.hasText(accessKeySecret)) {
logger.warn("短信服务未配置或未启用,使用模拟模式");
logger.info("【模拟发送】短信,手机号: {}, 模板: {}, 参数: {}", phone, templateCode, templateParam);
return true;
}
// 根据配置的服务商选择发送方式
String provider = smsConfigProperties.getProvider();
if (provider == null) provider = "aliyun";
switch (provider.toLowerCase()) {
case "aliyun":
return sendSmsAliyun(phone, templateCode, templateParam);
@@ -156,7 +149,7 @@ public class SmsUtils {
SendSmsRequest request = new SendSmsRequest()
.setPhoneNumbers(phone)
.setSignName(signName)
.setSignName(smsConfigProperties.getSignName())
.setTemplateCode(templateCode)
.setTemplateParam(templateParam);
@@ -185,13 +178,19 @@ public class SmsUtils {
*/
public boolean sendBatchSms(String phones, String templateCode, String templateParam) {
// 如果未启用短信服务,使用模拟模式
if (!enabled || !StringUtils.hasText(accessKeyId) || !StringUtils.hasText(accessKeySecret)) {
String accessKeyId = smsConfigProperties.getAccessKeyId();
String accessKeySecret = smsConfigProperties.getAccessKeySecret();
if (!StringUtils.hasText(accessKeyId) || !StringUtils.hasText(accessKeySecret)) {
logger.warn("短信服务未配置或未启用,使用模拟模式");
logger.info("【模拟发送】批量短信,手机号: {}, 模板: {}, 参数: {}", phones, templateCode, templateParam);
return true;
}
// 根据配置的服务商选择发送方式
String provider = smsConfigProperties.getProvider();
if (provider == null) provider = "aliyun";
switch (provider.toLowerCase()) {
case "aliyun":
return sendBatchSmsAliyun(phones, templateCode, templateParam);
@@ -214,7 +213,7 @@ public class SmsUtils {
SendSmsRequest request = new SendSmsRequest()
.setPhoneNumbers(phones)
.setSignName(signName)
.setSignName(smsConfigProperties.getSignName())
.setTemplateCode(templateCode)
.setTemplateParam(templateParam);

View File

@@ -217,35 +217,4 @@ public class MessageController {
return messageService.getUnreadCount();
}
// ================== 辅助接口 ==================
/**
* 获取可选的部门树
*
* @return ResultDomain<Map>
*/
@GetMapping("/targets/depts")
public ResultDomain<Map<String, Object>> getTargetDepts() {
return messageService.getTargetDepts();
}
/**
* 获取可选的角色列表
*
* @return ResultDomain<Map>
*/
@GetMapping("/targets/roles")
public ResultDomain<Map<String, Object>> getTargetRoles() {
return messageService.getTargetRoles();
}
/**
* 获取可选的用户列表
*
* @return ResultDomain<Map>
*/
@GetMapping("/targets/users")
public ResultDomain<Map<String, Object>> getTargetUsers() {
return messageService.getTargetUsers();
}
}

View File

@@ -0,0 +1,27 @@
package org.xyzh.message.event;
import org.springframework.context.ApplicationEvent;
/**
* 消息创建事件
* 用于在事务提交后触发异步发送
*
* @description 消息创建完成事件
* @filename MessageCreatedEvent.java
* @author yslg
* @copyright xyzh
* @since 2025-11-26
*/
public class MessageCreatedEvent extends ApplicationEvent {
private final String messageID;
public MessageCreatedEvent(Object source, String messageID) {
super(source);
this.messageID = messageID;
}
public String getMessageID() {
return messageID;
}
}

View File

@@ -6,11 +6,14 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;
import org.xyzh.common.dto.message.TbSysMessage;
import org.xyzh.common.dto.message.TbSysMessageUser;
import org.xyzh.common.dto.user.TbSysUser;
import org.xyzh.common.utils.EmailUtils;
import org.xyzh.common.utils.SmsUtils;
import org.xyzh.message.event.MessageCreatedEvent;
import org.xyzh.message.mapper.MessageMapper;
import org.xyzh.message.mapper.MessageUserMapper;
import org.xyzh.system.mapper.UserMapper;
@@ -47,6 +50,18 @@ public class MessageSendService {
@Autowired
private SmsUtils smsUtils;
/**
* 监听消息创建事件,在事务提交后触发异步发送
* 这样可以确保消息已经持久化到数据库,避免异步线程查询不到数据
*
* @param event 消息创建事件
*/
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void onMessageCreated(MessageCreatedEvent event) {
logger.info("接收到消息创建事件,准备异步发送消息:{}", event.getMessageID());
sendMessageAsync(event.getMessageID());
}
/**
* 异步发送消息
*
@@ -78,7 +93,6 @@ public class MessageSendService {
updateMessageStatus(message, "sent");
return;
}
// 3. 遍历发送
int successCount = 0;
int failedCount = 0;
@@ -280,8 +294,22 @@ public class MessageSendService {
html.append(message.getContent());
html.append("</div>");
html.append("<div class=\"footer\">");
html.append("<p>发送人:").append(message.getSenderName()).append(" (").append(message.getSenderDeptName()).append(")</p>");
html.append("<p>发送时间:").append(message.getActualSendTime() != null ? message.getActualSendTime().toString() : "").append("</p>");
// 发送人信息 - 安全处理null值
String senderName = message.getSenderName() != null ? message.getSenderName() : "系统";
String senderDept = message.getSenderDeptName() != null ? message.getSenderDeptName() : "";
if (senderDept.isEmpty()) {
html.append("<p>发送人:").append(senderName).append("</p>");
} else {
html.append("<p>发送人:").append(senderName).append(" (").append(senderDept).append(")</p>");
}
// 发送时间 - 使用实际发送时间或创建时间
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String sendTime = message.getActualSendTime() != null ?
sdf.format(message.getActualSendTime()) :
(message.getCreateTime() != null ? sdf.format(message.getCreateTime()) : "");
if (!sendTime.isEmpty()) {
html.append("<p>发送时间:").append(sendTime).append("</p>");
}
html.append("<p>此邮件由系统自动发送,请勿回复。</p>");
html.append("</div>");
html.append("</div>");

View File

@@ -1,21 +1,25 @@
package org.xyzh.message.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xyzh.api.message.MessageService;
import org.xyzh.common.core.domain.LoginDomain;
import org.xyzh.common.core.domain.ResultDomain;
import org.xyzh.common.core.page.PageDomain;
import org.xyzh.common.core.page.PageParam;
import org.xyzh.common.dto.message.*;
import org.xyzh.common.utils.IDUtils;
import org.xyzh.message.event.MessageCreatedEvent;
import org.xyzh.message.mapper.MessageMapper;
import org.xyzh.message.mapper.MessageTargetMapper;
import org.xyzh.message.mapper.MessageUserMapper;
import org.xyzh.system.mapper.DepartmentMapper;
import org.xyzh.system.mapper.UserMapper;
import org.xyzh.system.utils.LoginUtil;
import java.util.*;
@@ -51,6 +55,9 @@ public class MessageServiceImpl implements MessageService {
@Autowired
private MessageSendService messageSendService;
@Autowired
private ApplicationEventPublisher eventPublisher;
/**
* 创建消息
*/
@@ -60,8 +67,9 @@ public class MessageServiceImpl implements MessageService {
ResultDomain<TbSysMessage> rt = new ResultDomain<>();
try {
// 1. 获取当前用户信息从Session或SecurityContext获取
String currentUserID = getCurrentUserID();
String currentDeptID = getCurrentUserDeptID();
LoginDomain currentUser = LoginUtil.getCurrentLoginDomain();
String currentUserID = currentUser.getUser().getID();
String currentDeptID = currentUser.getRoles().get(0).getDeptID();
// 2. 设置消息主体基本信息
if (message.getID() == null) {
@@ -72,6 +80,7 @@ public class MessageServiceImpl implements MessageService {
}
message.setSenderID(currentUserID);
message.setSenderDeptID(currentDeptID);
message.setSenderName(currentUser.getUser().getUsername());
// 设置状态
if (message.getStatus() == null) {
@@ -104,10 +113,11 @@ public class MessageServiceImpl implements MessageService {
if (targets != null && !targets.isEmpty()) {
for (TbSysMessageTarget target : targets) {
// 权限校验scopeDeptID必须是当前部门或子部门
if (!isCurrentOrSubDept(currentDeptID, target.getScopeDeptID())) {
rt.fail("无权向该部门发送消息");
return rt;
}
// TODO: 实现部门层级检查
// if (!isCurrentOrSubDept(currentDeptID, target.getScopeDeptID())) {
// rt.fail("无权向该部门发送消息");
// return rt;
// }
if (target.getID() == null) {
target.setID(IDUtils.generateID());
@@ -130,9 +140,10 @@ public class MessageServiceImpl implements MessageService {
message.setTargetUserCount(userMessages.size());
messageMapper.updateMessage(message);
// 6. 如果是立即发送,异步发送消息
// 6. 如果是立即发送,发布事件触发异步发送
// 使用事件机制确保在事务提交后再发送,避免异步线程读不到数据
if ("immediate".equals(message.getSendMode())) {
messageSendService.sendMessageAsync(message.getMessageID());
eventPublisher.publishEvent(new MessageCreatedEvent(this, message.getMessageID()));
}
}
@@ -168,7 +179,8 @@ public class MessageServiceImpl implements MessageService {
}
message.setUpdateTime(new Date());
message.setUpdater(getCurrentUserID());
LoginDomain currentUser = LoginUtil.getCurrentLoginDomain();
message.setUpdater(currentUser.getUser().getID());
messageMapper.updateMessage(message);
rt.success("更新成功", message);
return rt;
@@ -259,7 +271,8 @@ public class MessageServiceImpl implements MessageService {
pageParam = new PageParam();
}
String currentDeptID = getCurrentUserDeptID();
LoginDomain currentUser = LoginUtil.getCurrentLoginDomain();
String currentDeptID = currentUser.getRoles().get(0).getDeptID();
List<MessageVO> list = messageMapper.selectMessagePage(filter, currentDeptID);
int total = messageMapper.countMessage(filter, currentDeptID);
@@ -475,7 +488,8 @@ public class MessageServiceImpl implements MessageService {
pageParam = new PageParam();
}
String currentUserID = getCurrentUserID();
LoginDomain currentUser = LoginUtil.getCurrentLoginDomain();
String currentUserID = currentUser.getUser().getID();
// 使用新的动态查询方法
List<MessageUserVO> list = messageUserMapper.selectMyMessagesWithDynamicTargets(currentUserID, filter);
@@ -500,7 +514,8 @@ public class MessageServiceImpl implements MessageService {
public ResultDomain<MessageUserVO> getMyMessageDetail(String messageID) {
ResultDomain<MessageUserVO> rt = new ResultDomain<>();
try {
String currentUserID = getCurrentUserID();
LoginDomain currentUser = LoginUtil.getCurrentLoginDomain();
String currentUserID = currentUser.getUser().getID();
MessageUserVO messageUserVO = messageUserMapper.selectOrCreateUserMessage(currentUserID, messageID);
if (messageUserVO == null) {
@@ -546,7 +561,8 @@ public class MessageServiceImpl implements MessageService {
public ResultDomain<TbSysMessageUser> markAsRead(String messageID) {
ResultDomain<TbSysMessageUser> rt = new ResultDomain<>();
try {
String currentUserID = getCurrentUserID();
LoginDomain currentUser = LoginUtil.getCurrentLoginDomain();
String currentUserID = currentUser.getUser().getID();
// 先尝试更新已有记录
int result = messageUserMapper.markAsRead(currentUserID, messageID);
@@ -599,7 +615,8 @@ public class MessageServiceImpl implements MessageService {
public ResultDomain<Integer> batchMarkAsRead(List<String> messageIDs) {
ResultDomain<Integer> rt = new ResultDomain<>();
try {
String currentUserID = getCurrentUserID();
LoginDomain currentUser = LoginUtil.getCurrentLoginDomain();
String currentUserID = currentUser.getUser().getID();
int count = messageUserMapper.batchMarkAsRead(currentUserID, messageIDs);
// 更新每条消息的已读数量
@@ -621,7 +638,8 @@ public class MessageServiceImpl implements MessageService {
public ResultDomain<Integer> getUnreadCount() {
ResultDomain<Integer> rt = new ResultDomain<>();
try {
String currentUserID = getCurrentUserID();
LoginDomain currentUser = LoginUtil.getCurrentLoginDomain();
String currentUserID = currentUser.getUser().getID();
// 使用动态计算方法,统计用户应该看到的所有未读消息
Integer count = messageUserMapper.countUnreadWithDynamicTargets(currentUserID);
rt.success("查询成功", count != null ? count : 0);
@@ -636,29 +654,7 @@ public class MessageServiceImpl implements MessageService {
// ================== 辅助方法 ==================
@Override
public ResultDomain<Map<String, Object>> getTargetDepts() {
ResultDomain<Map<String, Object>> rt = new ResultDomain<>();
// TODO: 实现获取可选部门树
rt.success("查询成功", new HashMap<>());
return rt;
}
@Override
public ResultDomain<Map<String, Object>> getTargetRoles() {
ResultDomain<Map<String, Object>> rt = new ResultDomain<>();
// TODO: 实现获取可选角色列表
rt.success("查询成功", new HashMap<>());
return rt;
}
@Override
public ResultDomain<Map<String, Object>> getTargetUsers() {
ResultDomain<Map<String, Object>> rt = new ResultDomain<>();
// TODO: 实现获取可选用户列表
rt.success("查询成功", new HashMap<>());
return rt;
}
// ================== 私有辅助方法 ==================
@@ -720,27 +716,4 @@ public class MessageServiceImpl implements MessageService {
return userMessages;
}
/**
* 检查目标部门是否是当前部门或子部门
*/
private boolean isCurrentOrSubDept(String currentDeptID, String targetDeptID) {
// TODO: 实现部门层级检查
return true;
}
/**
* 获取当前用户ID
*/
private String getCurrentUserID() {
// TODO: 从SecurityContext或Session获取
return "1";
}
/**
* 获取当前用户部门ID
*/
private String getCurrentUserDeptID() {
// TODO: 从SecurityContext或Session获取
return "root_department";
}
}

View File

@@ -166,12 +166,12 @@
<insert id="insertMessage">
INSERT INTO tb_sys_message
(id, message_id, title, content, message_type, priority, sender_id, sender_dept_id,
send_mode, scheduled_time, status, target_user_count, retry_count, max_retry_count,
send_mode, sender_name,scheduled_time, status, target_user_count, retry_count, max_retry_count,
creator, create_time, update_time, deleted)
VALUES
(#{message.id}, #{message.messageID}, #{message.title}, #{message.content},
#{message.messageType}, #{message.priority}, #{message.senderID}, #{message.senderDeptID},
#{message.sendMode}, #{message.scheduledTime}, #{message.status}, #{message.targetUserCount},
#{message.sendMode}, #{message.senderName},#{message.scheduledTime}, #{message.status}, #{message.targetUserCount},
#{message.retryCount}, #{message.maxRetryCount}, #{message.creator}, #{message.createTime},
#{message.updateTime}, #{message.deleted})
</insert>

View File

@@ -0,0 +1,135 @@
package org.xyzh.system.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.util.StringUtils;
import org.xyzh.api.system.config.SysConfigService;
import org.xyzh.common.config.properties.EmailConfigProperties;
import org.xyzh.common.config.properties.SmsConfigProperties;
import java.util.Properties;
/**
* @description 动态配置加载器 - 从数据库加载配置并应用到组件
* @filename DynamicConfigLoader.java
* @author yslg
* @copyright xyzh
* @since 2025-11-26
*/
@Configuration
@Order(100) // 确保在其他组件初始化之后再加载配置
public class DynamicConfigLoader implements ApplicationRunner {
private static final Logger logger = LoggerFactory.getLogger(DynamicConfigLoader.class);
@Autowired
private SysConfigService sysConfigService;
@Autowired(required = false)
private JavaMailSenderImpl mailSender;
@Autowired
private EmailConfigProperties emailConfigProperties;
@Autowired
private SmsConfigProperties smsConfigProperties;
@Override
public void run(ApplicationArguments args) throws Exception {
logger.info("=== 开始加载动态配置 ===");
// 加载邮件配置
loadEmailConfig();
// 加载短信配置
loadSmsConfig();
logger.info("=== 动态配置加载完成 ===");
}
/**
* 加载邮件配置
*/
private void loadEmailConfig() {
try {
String host = sysConfigService.getStringConfig("email.host");
String port = sysConfigService.getStringConfig("email.port");
String username = sysConfigService.getStringConfig("email.username");
String password = sysConfigService.getStringConfig("email.password");
String fromName = sysConfigService.getStringConfig("email.fromName");
String sslEnable = sysConfigService.getStringConfig("email.ssl.enable");
String timeout = sysConfigService.getStringConfig("email.timeout");
// 更新配置属性
emailConfigProperties.setHost(host);
emailConfigProperties.setPort(StringUtils.hasText(port) ? Integer.valueOf(port) : 587);
emailConfigProperties.setUsername(username);
emailConfigProperties.setPassword(password);
emailConfigProperties.setFromName(fromName);
emailConfigProperties.setSslEnable("true".equalsIgnoreCase(sslEnable));
emailConfigProperties.setTimeout(StringUtils.hasText(timeout) ? Integer.valueOf(timeout) : 30000);
// 如果邮箱配置完整则配置JavaMailSender
if (mailSender != null && StringUtils.hasText(host) && StringUtils.hasText(username) && StringUtils.hasText(password)) {
mailSender.setHost(host);
mailSender.setPort(emailConfigProperties.getPort());
mailSender.setUsername(username);
mailSender.setPassword(password);
// 设置邮件属性
Properties props = mailSender.getJavaMailProperties();
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", emailConfigProperties.getSslEnable() ? "true" : "false");
props.put("mail.smtp.starttls.required", emailConfigProperties.getSslEnable() ? "true" : "false");
props.put("mail.smtp.timeout", emailConfigProperties.getTimeout());
props.put("mail.smtp.connectiontimeout", emailConfigProperties.getTimeout());
logger.info("邮件配置加载成功: host={}, port={}, username={}", host, emailConfigProperties.getPort(), username);
} else {
logger.warn("邮件配置不完整,将使用默认配置或模拟模式");
}
} catch (Exception e) {
logger.error("加载邮件配置失败", e);
}
}
/**
* 加载短信配置
*/
private void loadSmsConfig() {
try {
String provider = sysConfigService.getStringConfig("sms.provider");
String accessKeyId = sysConfigService.getStringConfig("sms.accessKeyId");
String accessKeySecret = sysConfigService.getStringConfig("sms.accessKeySecret");
String signName = sysConfigService.getStringConfig("sms.signName");
String templateCodeLogin = sysConfigService.getStringConfig("sms.templateCode.login");
String templateCodeRegister = sysConfigService.getStringConfig("sms.templateCode.register");
String timeout = sysConfigService.getStringConfig("sms.timeout");
// 更新配置属性
smsConfigProperties.setProvider(StringUtils.hasText(provider) ? provider : "aliyun");
smsConfigProperties.setAccessKeyId(accessKeyId);
smsConfigProperties.setAccessKeySecret(accessKeySecret);
smsConfigProperties.setSignName(StringUtils.hasText(signName) ? signName : "校园新闻");
smsConfigProperties.setTemplateCodeLogin(templateCodeLogin);
smsConfigProperties.setTemplateCodeRegister(templateCodeRegister);
smsConfigProperties.setTimeout(StringUtils.hasText(timeout) ? Integer.valueOf(timeout) : 30000);
if (StringUtils.hasText(accessKeyId) && StringUtils.hasText(accessKeySecret)) {
logger.info("短信配置加载成功: provider={}, signName={}", smsConfigProperties.getProvider(), smsConfigProperties.getSignName());
} else {
logger.warn("短信配置不完整,将使用模拟模式");
}
} catch (Exception e) {
logger.error("加载短信配置失败", e);
}
}
}

View File

@@ -720,7 +720,7 @@ public class SysUserServiceImpl implements SysUserService {
TbSysUser user = existResult.getData();
// TODO: 这里应该对密码进行加密处理
user.setPassword(newPassword);
user.setPassword(passwordEncoder.encode(newPassword));
user.setUpdateTime(new Date());
int result = userMapper.updateUser(user);

View File

@@ -249,11 +249,16 @@ public class UserProfileController {
logger.info("用户绑定手机号: userId={}, phone={}", userId, phone);
// TODO: 验证手机验证码
// TODO: 检查手机号是否已被其他用户绑定
// TODO: 更新用户手机号
resultDomain.fail("功能开发中");
TbSysUser user = new TbSysUser();
user.setID(userId);
user.setPhone(phone);
ResultDomain<TbSysUser> bindResult = userService.updateUser(user);
if (bindResult.isSuccess()) {
resultDomain.success("手机号绑定成功", true);
}else{
resultDomain.fail(bindResult.getMessage());
}
return resultDomain;
} catch (Exception e) {
@@ -284,11 +289,16 @@ public class UserProfileController {
logger.info("用户绑定邮箱: userId={}, email={}", userId, email);
// TODO: 验证邮箱验证码
// TODO: 检查邮箱是否已被其他用户绑定
// TODO: 更新用户邮箱
resultDomain.fail("功能开发中");
TbSysUser user = new TbSysUser();
user.setID(userId);
user.setEmail(email);
ResultDomain<TbSysUser> bindResult = userService.updateUser(user);
if (bindResult.isSuccess()) {
resultDomain.success("邮箱绑定成功", true);
}else {
resultDomain.fail(bindResult.getMessage());
}
return resultDomain;
} catch (Exception e) {